package com.smqjh.agent.mcp;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.ArrayList;
import java.util.List;

final class ToolRegistry {
    private final AppConfig config;
    private final ReadOnlyDatabaseClient database;
    private final SmqjhCloudClient cloud;
    private final MonthlySettlementPlanner settlementPlanner;
    private final DeepSeekMcpClient deepSeek;
    private final BusinessSchemaRegistry schema;

    ToolRegistry(AppConfig config) {
        this.config = config;
        this.database = new ReadOnlyDatabaseClient(config);
        this.cloud = new SmqjhCloudClient(config);
        this.settlementPlanner = new MonthlySettlementPlanner();
        this.deepSeek = new DeepSeekMcpClient(config);
        this.schema = new BusinessSchemaRegistry();
    }

    List<ObjectNode> listTools() {
        List<ObjectNode> tools = new ArrayList<>();
        tools.add(tool("smqjh.config.get", "读取本机运行配置", "返回网关、数据库、DeepSeek、MCP 状态。敏感字段会脱敏。", objectSchema(Jsons.object())));
        tools.add(tool("smqjh.cloud.health", "网关连通检查", "调用验证码接口检查 smqjh 网关是否可达。", objectSchema(Jsons.object())));
        tools.add(tool("smqjh.schema.search", "搜索业务表和规则", "按自然语言搜索 smqjh 业务表、字段、业务接口和查询口径。", objectSchema(requiredProps("query", "string", "业务问题、表名或字段关键词"), "query")));
        tools.add(tool("smqjh.schema.getTable", "读取业务表说明", "返回单张业务表的字段说明、接口来源和示例 SQL。", objectSchema(requiredProps("table", "string", "表名，例如 smqjh_oms.oms_order"), "table")));
        tools.add(tool("smqjh.schema.businessRules", "读取业务规则", "返回订单、商品、会员、结算、充值、消费等业务口径。", objectSchema(requiredProps("topic", "string", "业务主题，例如 月结/订单/商品/充值"), "topic")));
        tools.add(tool("smqjh.database.readonly.query", "执行只读 SQL", "只允许 SELECT，自动校验危险语句、表白名单和默认 LIMIT。", objectSchema(requiredProps("sql", "string", "只读 SELECT SQL"), "sql")));
        tools.add(tool("smqjh.database.smart.query", "智能数据库查询", "输入管理员自然语言问题，由 DeepSeek 结合业务 schema 生成安全 SELECT，经白名单和只读校验后自动执行并返回表格。", objectSchema(smartQueryProps(), "question")));
        tools.add(tool("smqjh.order.count.query", "订单统计查询", "按手机号和日期区间统计订单数量、销售额、已付/未付金额，只读查询数据库。", objectSchema(orderCountProps())));
        tools.add(tool("smqjh.product.lookup.summary", "商品资料查询", "按商品关键词查询商品名、品牌、价格、状态、SKU 和描述。", objectSchema(requiredProps("productKeyword", "string", "商品关键词"), "productKeyword")));
        tools.add(tool("smqjh.settlement.enterprise.list", "月结企业清单", "返回当前已确认的企业月结渠道清单和结算范围。", objectSchema(Jsons.object())));
        tools.add(tool("smqjh.settlement.monthly.plan", "企业月结处理计划", "根据企业和月份返回月结导出接口、参数、核对规则和人工确认条件。", objectSchema(settlementProps(), "enterprise", "month")));
        tools.add(tool("smqjh.settlement.monthly.preview", "企业月结只读预览", "根据企业和月份直接只读查询订单、商品、充值和积分流水，返回月结汇总表与商品汇总表；只标记需人工确认，不写入业务库。", objectSchema(settlementProps(), "enterprise", "month")));
        tools.add(tool("smqjh.ai.tool.plan", "DeepSeek 工具规划", "由 MCP 调用 DeepSeek 判断管理员问题需要调用哪些工具。", objectSchema(aiPlanProps(), "request", "tools")));
        tools.add(tool("smqjh.ai.tool.summarize", "DeepSeek 工具结果总结", "由 MCP 调用 DeepSeek 汇总工具执行结果，生成面向管理员的回答。", objectSchema(aiSummarizeProps(), "request", "observations")));
        tools.add(tool("smqjh.ai.chat", "DeepSeek 普通对话", "由 MCP 调用 DeepSeek 处理范围内的简单问答。", objectSchema(aiChatProps(), "request")));
        return tools;
    }

    ObjectNode call(JsonNode params) throws Exception {
        String name = params.path("name").asText("");
        JsonNode args = params.path("arguments").isObject() ? params.path("arguments") : Jsons.object();
        Object result = switch (name) {
            case "smqjh.config.get" -> maskedConfig();
            case "smqjh.cloud.health" -> cloud.health();
            case "smqjh.schema.search" -> schema.search(requiredText(args, "query"));
            case "smqjh.schema.getTable" -> schema.getTable(requiredText(args, "table"));
            case "smqjh.schema.businessRules" -> schema.businessRules(requiredText(args, "topic"));
            case "smqjh.database.readonly.query" -> database.runReadOnlyQuery(requiredText(args, "sql"));
            case "smqjh.database.smart.query" -> smartQuery(args);
            case "smqjh.order.count.query" -> database.countOrders(text(args, "mobile"), text(args, "dateFrom"), text(args, "dateTo"));
            case "smqjh.product.lookup.summary" -> database.productLookupSummary(requiredText(args, "productKeyword"));
            case "smqjh.settlement.enterprise.list" -> settlementPlanner.enterprises();
            case "smqjh.settlement.monthly.plan" -> settlementPlanner.plan(requiredText(args, "enterprise"), requiredText(args, "month"));
            case "smqjh.settlement.monthly.preview" -> database.monthlySettlementPreview(requiredText(args, "enterprise"), requiredText(args, "month"));
            case "smqjh.ai.tool.plan" -> deepSeek.planToolUse(args);
            case "smqjh.ai.tool.summarize" -> deepSeek.summarizeToolResults(args);
            case "smqjh.ai.chat" -> deepSeek.chat(args);
            default -> throw new IllegalArgumentException("Unknown tool: " + name);
        };
        ObjectNode payload = Jsons.object();
        ArrayNode content = Jsons.MAPPER.createArrayNode();
        ObjectNode text = Jsons.object();
        text.put("type", "text");
        text.put("text", Jsons.stringify(result));
        content.add(text);
        payload.set("content", content);
        payload.set("structuredContent", Jsons.MAPPER.valueToTree(result));
        return payload;
    }

    private ObjectNode smartQuery(JsonNode args) throws Exception {
        String question = requiredText(args, "question");
        ObjectNode plan = deepSeek.generateReadOnlySql(question, schema.promptContext(), args);
        String sql = plan.path("sql").asText("").trim();
        if (sql.isBlank()) {
            throw new IllegalArgumentException("DeepSeek 未生成可执行 SELECT");
        }
        boolean repaired = false;
        String firstError = "";
        ObjectNode queryResult;
        try {
            queryResult = database.runReadOnlyQuery(sql);
        } catch (Exception error) {
            firstError = error.getMessage();
            ObjectNode retryArgs = Jsons.object();
            retryArgs.put("context", String.join("\n",
                "第一次 SQL 执行失败，需要重新生成更稳妥的 SELECT。",
                "失败 SQL: " + sql,
                "数据库错误: " + firstError,
                "要求：优先使用 schema 中明确列出的字段；如果字段不确定，用更少字段或换关联口径；仍然只输出 JSON 和单条 SELECT。"
            ));
            ObjectNode retryPlan = deepSeek.generateReadOnlySql(question, schema.promptContext(), retryArgs);
            String retrySql = retryPlan.path("sql").asText("").trim();
            if (retrySql.isBlank() || retrySql.equalsIgnoreCase(sql)) {
                throw error;
            }
            plan = retryPlan;
            sql = retrySql;
            repaired = true;
            queryResult = database.runReadOnlyQuery(sql);
        }
        ObjectNode result = Jsons.object();
        result.put("ok", queryResult.path("ok").asBoolean(false));
        result.put("question", question);
        result.put("title", plan.path("title").asText("智能数据库查询结果"));
        result.put("summary", plan.path("summary").asText(""));
        result.put("generatedSql", sql);
        result.put("executedSql", queryResult.path("executedSql").asText(""));
        result.put("sqlRepaired", repaired);
        if (!firstError.isBlank()) {
            result.put("firstSqlError", firstError);
        }
        result.put("rowCount", queryResult.path("rowCount").asInt(0));
        result.set("columns", queryResult.path("columns"));
        result.set("rows", queryResult.path("rows"));
        result.set("warnings", plan.path("warnings").isArray() ? plan.path("warnings") : Jsons.MAPPER.createArrayNode());
        result.put("evidence", "MCP 工具 smqjh.database.smart.query；DeepSeek 生成 SELECT；ReadOnlyDatabaseClient 执行白名单、只读、LIMIT、超时校验");
        return result;
    }

    private ObjectNode maskedConfig() {
        ObjectNode root = Jsons.object();
        root.put("environmentName", config.environmentName);
        root.put("baseUrl", config.baseUrl);
        root.put("desktopConfigPath", config.desktopConfigPath);
        ObjectNode auth = Jsons.object();
        auth.put("tokenPath", config.auth.tokenPath);
        auth.put("clientId", config.auth.clientId);
        auth.put("topAdminNoTenantCode", true);
        auth.put("tenantCode", mask(config.tenantCode));
        auth.put("tenantDomain", config.tenantDomain);
        root.set("auth", auth);
        ObjectNode db = Jsons.object();
        db.put("enabled", config.database.enabled);
        db.put("host", mask(config.database.host));
        db.put("port", config.database.port);
        db.put("database", config.database.database);
        db.put("username", mask(config.database.username));
        root.set("database", db);
        ObjectNode deepSeek = Jsons.object();
        deepSeek.put("enabled", config.deepSeek.enabled);
        deepSeek.put("baseURL", config.deepSeek.baseURL);
        deepSeek.put("model", config.deepSeek.model);
        deepSeek.put("apiKeyConfigured", config.deepSeek.apiKey != null && !config.deepSeek.apiKey.isBlank());
        root.set("deepSeek", deepSeek);
        ObjectNode mcp = Jsons.object();
        mcp.put("enabled", config.mcp.enabled);
        mcp.put("host", config.mcp.allowExternal ? config.mcp.host : "127.0.0.1");
        mcp.put("port", config.mcp.port);
        mcp.put("path", config.mcp.path);
        mcp.put("tokenConfigured", config.mcp.authToken != null && !config.mcp.authToken.isBlank());
        root.set("mcp", mcp);
        return root;
    }

    private ObjectNode tool(String name, String title, String description, ObjectNode inputSchema) {
        ObjectNode tool = Jsons.object();
        tool.put("name", name);
        tool.put("title", title);
        tool.put("description", description);
        tool.set("inputSchema", inputSchema);
        return tool;
    }

    private ObjectNode objectSchema(ObjectNode properties, String... required) {
        ObjectNode schema = Jsons.object();
        schema.put("type", "object");
        schema.set("properties", properties);
        ArrayNode requiredNode = Jsons.MAPPER.createArrayNode();
        for (String key : required) {
            requiredNode.add(key);
        }
        schema.set("required", requiredNode);
        schema.put("additionalProperties", false);
        return schema;
    }

    private ObjectNode requiredProps(String key, String type, String description) {
        ObjectNode props = Jsons.object();
        ObjectNode prop = Jsons.object();
        prop.put("type", type);
        prop.put("description", description);
        props.set(key, prop);
        return props;
    }

    private ObjectNode orderCountProps() {
        ObjectNode props = Jsons.object();
        props.set("mobile", prop("string", "可选手机号"));
        props.set("dateFrom", prop("string", "可选开始日期，YYYY-MM-DD"));
        props.set("dateTo", prop("string", "可选结束日期，YYYY-MM-DD，左闭右开"));
        return props;
    }

    private ObjectNode smartQueryProps() {
        ObjectNode props = Jsons.object();
        props.set("question", prop("string", "管理员自然语言问题，例如：当前百事可乐 500ml 的商品描述是什么、查询订单物流状态、某手机号下了多少单"));
        props.set("context", prop("string", "可选补充上下文，例如上轮结果、企业、月份、订单号"));
        return props;
    }

    private ObjectNode settlementProps() {
        ObjectNode props = Jsons.object();
        props.set("enterprise", prop("string", "企业名称、渠道号或 channelId，例如 铜仁移动 / 5 / 58"));
        props.set("month", prop("string", "结算月份，格式 YYYY-MM"));
        return props;
    }

    private ObjectNode aiPlanProps() {
        ObjectNode props = Jsons.object();
        props.set("request", prop("object", "AssistantRunRequest，包含 message、history、登录上下文等"));
        props.set("tools", prop("array", "可供 DeepSeek 选择的工具列表"));
        props.set("observations", prop("array", "已执行工具结果，可选"));
        return props;
    }

    private ObjectNode aiSummarizeProps() {
        ObjectNode props = Jsons.object();
        props.set("request", prop("object", "AssistantRunRequest"));
        props.set("observations", prop("array", "已执行工具的结果列表"));
        return props;
    }

    private ObjectNode aiChatProps() {
        ObjectNode props = Jsons.object();
        props.set("request", prop("object", "AssistantRunRequest"));
        return props;
    }

    private ObjectNode prop(String type, String description) {
        ObjectNode prop = Jsons.object();
        prop.put("type", type);
        prop.put("description", description);
        return prop;
    }

    private String requiredText(JsonNode args, String key) {
        String value = text(args, key);
        if (value.isBlank()) {
            throw new IllegalArgumentException("Missing argument: " + key);
        }
        return value;
    }

    private String text(JsonNode args, String key) {
        JsonNode value = args.path(key);
        return value.isMissingNode() || value.isNull() ? "" : value.asText("").trim();
    }

    private String mask(String value) {
        if (value == null || value.isBlank()) {
            return "";
        }
        if (value.length() <= 4) {
            return "***";
        }
        return value.substring(0, 2) + "***" + value.substring(value.length() - 2);
    }
}
