package com.gzzm.lobster.api.admin;

import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.common.McpTransportType;
import com.gzzm.lobster.common.ToolRiskLevel;
import com.gzzm.lobster.config.LobsterConfig;
import com.gzzm.lobster.identity.AdminGuard;
import com.gzzm.lobster.identity.UserContext;
import com.gzzm.lobster.tool.mcp.McpServerConfig;
import com.gzzm.lobster.tool.mcp.McpServerConfigDao;
import com.gzzm.lobster.tool.mcp.McpToolBridge;
import com.gzzm.lobster.tool.mcp.McpToolCacheDao;
import net.cyan.arachne.HttpMethod;
import net.cyan.arachne.annotation.Service;
import net.cyan.nest.annotation.Inject;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * AdminMcpServerApi —— MCP Server 后台 CRUD / MCP server admin CRUD.
 */
@Service
public class AdminMcpServerApi {

    @Inject private McpServerConfigDao mcpDao;
    @Inject private McpToolBridge mcpToolBridge;
    @Inject private McpToolCacheDao mcpToolCacheDao;

    @Service(url = "/ai/api/admin/mcp-servers/list", method = HttpMethod.all)
    public Map<String, Object> list(Integer offset, Integer limit) throws Exception {
        AdminGuard.requireAdmin();
        int o = offset == null ? 0 : Math.max(0, offset);
        int l = limit == null
                ? LobsterConfig.getListDefaultPageSize()
                : Math.min(LobsterConfig.getListMaxPageSize(), Math.max(1, limit));
        List<McpServerConfig> rows = mcpDao.listAll(o, l);
        List<Map<String, Object>> items = new ArrayList<>();
        for (McpServerConfig s : rows) items.add(toMap(s));
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("items", items);
        out.put("total", mcpDao.countAll());
        out.put("offset", o);
        out.put("limit", l);
        return out;
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}", method = HttpMethod.all)
    public Map<String, Object> get(String serverId) throws Exception {
        AdminGuard.requireAdmin();
        McpServerConfig s = mcpDao.getConfig(serverId);
        if (s == null) throw new LobsterException("admin.mcp.not_found", "Mcp server not found: " + serverId);
        return toMap(s);
    }

    @Service(url = "/ai/api/admin/mcp-servers/create", method = HttpMethod.post)
    public Map<String, Object> create(String serverId, String displayName, String transportType,
                                      String endpoint, String namespace, String defaultRisk,
                                      Boolean enabled, String authToken, String headersJson,
                                      Integer connectTimeoutMs, Integer readTimeoutMs) throws Exception {
        UserContext admin = AdminGuard.requireAdmin();
        if (serverId == null || serverId.trim().isEmpty()) {
            throw new LobsterException("admin.mcp.invalid", "serverId required");
        }
        if (transportType == null || endpoint == null || namespace == null) {
            throw new LobsterException("admin.mcp.invalid", "transportType/endpoint/namespace required");
        }
        if (mcpDao.getConfig(serverId) != null) {
            throw new LobsterException("admin.mcp.conflict", "Mcp server already exists: " + serverId);
        }
        McpServerConfig s = new McpServerConfig();
        s.setServerId(serverId);
        s.setDisplayName(displayName);
        s.setTransportType(McpTransportType.valueOf(transportType));
        s.setEndpoint(endpoint);
        s.setAuthToken(authToken);
        s.setHeadersJson(headersJson);
        s.setConnectTimeoutMs(connectTimeoutMs == null ? Integer.valueOf(5000) : connectTimeoutMs);
        s.setReadTimeoutMs(readTimeoutMs == null ? Integer.valueOf(30000) : readTimeoutMs);
        s.setNamespace(namespace);
        s.setDefaultRisk(defaultRisk == null ? ToolRiskLevel.WRITE : ToolRiskLevel.valueOf(defaultRisk));
        s.setEnabled(enabled == null ? Boolean.TRUE : enabled);
        s.setOrgId(admin.getOrgId());
        s.setCreateTime(new Date());
        s.setUpdateTime(new Date());
        mcpDao.save(s);
        if (Boolean.TRUE.equals(s.getEnabled())) {
            mcpToolBridge.discoverServer(serverId, false);
        }
        return toMap(s);
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}/update", method = HttpMethod.post)
    public Map<String, Object> update(String serverId, String displayName, String transportType,
                                      String endpoint, String namespace, String defaultRisk,
                                      Boolean enabled, String authToken, String headersJson,
                                      Integer connectTimeoutMs, Integer readTimeoutMs) throws Exception {
        AdminGuard.requireAdmin();
        McpServerConfig s = mcpDao.getConfig(serverId);
        if (s == null) throw new LobsterException("admin.mcp.not_found", "Mcp server not found: " + serverId);
        boolean wasEnabled = Boolean.TRUE.equals(s.getEnabled());
        String oldNamespace = s.getNamespace();
        String oldEndpoint = s.getEndpoint();
        McpTransportType oldTransportType = s.getTransportType();
        if (displayName != null) s.setDisplayName(displayName);
        if (transportType != null) s.setTransportType(McpTransportType.valueOf(transportType));
        if (endpoint != null) s.setEndpoint(endpoint);
        if (authToken != null) s.setAuthToken(authToken);
        if (headersJson != null) s.setHeadersJson(headersJson);
        if (connectTimeoutMs != null) s.setConnectTimeoutMs(connectTimeoutMs);
        if (readTimeoutMs != null) s.setReadTimeoutMs(readTimeoutMs);
        if (namespace != null) s.setNamespace(namespace);
        if (defaultRisk != null) s.setDefaultRisk(ToolRiskLevel.valueOf(defaultRisk));
        if (enabled != null) s.setEnabled(enabled);
        s.setUpdateTime(new Date());
        mcpDao.save(s);
        boolean nowEnabled = Boolean.TRUE.equals(s.getEnabled());
        boolean discoveryRelevantChanged = !Objects.equals(oldNamespace, s.getNamespace())
                || !Objects.equals(oldEndpoint, s.getEndpoint())
                || !Objects.equals(oldTransportType, s.getTransportType());
        if (!nowEnabled) {
            mcpToolBridge.unregisterServer(serverId);
        } else if (!wasEnabled || discoveryRelevantChanged) {
            mcpToolBridge.unregisterServer(serverId);
            mcpToolBridge.discoverServer(serverId, true);
        } else {
            mcpToolBridge.refreshRuntimeServer(serverId);
        }
        return toMap(s);
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}/delete", method = HttpMethod.post)
    public Map<String, Object> delete(String serverId) throws Exception {
        AdminGuard.requireAdmin();
        mcpToolBridge.unregisterServer(serverId);
        mcpToolCacheDao.deleteByServer(serverId);
        int n = mcpDao.deleteById(serverId);
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("deleted", n);
        return out;
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}/enabled", method = HttpMethod.post)
    public Map<String, Object> toggleEnabled(String serverId, Boolean enabled) throws Exception {
        AdminGuard.requireAdmin();
        McpServerConfig s = mcpDao.getConfig(serverId);
        if (s == null) throw new LobsterException("admin.mcp.not_found", "Mcp server not found: " + serverId);
        s.setEnabled(enabled == null ? !Boolean.TRUE.equals(s.getEnabled()) : enabled);
        s.setUpdateTime(new Date());
        mcpDao.save(s);
        if (Boolean.FALSE.equals(s.getEnabled())) {
            mcpToolBridge.unregisterServer(serverId);
        } else {
            mcpToolBridge.discoverServer(serverId, false);
        }
        return toMap(s);
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}/test", method = HttpMethod.post)
    public Map<String, Object> test(String serverId) throws Exception {
        AdminGuard.requireAdmin();
        return mcpToolBridge.testServer(serverId);
    }

    @Service(url = "/ai/api/admin/mcp-servers/{$0}/discover", method = HttpMethod.post)
    public Map<String, Object> discover(String serverId, Boolean force) throws Exception {
        AdminGuard.requireAdmin();
        return mcpToolBridge.discoverServer(serverId, Boolean.TRUE.equals(force)).toMap();
    }

    private static Map<String, Object> toMap(McpServerConfig s) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("serverId", s.getServerId());
        m.put("displayName", s.getDisplayName());
        m.put("transportType", s.getTransportType());
        m.put("endpoint", s.getEndpoint());
        m.put("authTokenSet", s.getAuthToken() != null && !s.getAuthToken().isEmpty());
        m.put("headersJson", s.getHeadersJson());
        m.put("connectTimeoutMs", s.getConnectTimeoutMs());
        m.put("readTimeoutMs", s.getReadTimeoutMs());
        m.put("namespace", s.getNamespace());
        m.put("defaultRisk", s.getDefaultRisk());
        m.put("enabled", s.getEnabled());
        m.put("orgId", s.getOrgId());
        m.put("createTime", s.getCreateTime());
        m.put("updateTime", s.getUpdateTime());
        return m;
    }
}
