package com.gzzm.lobster.api.admin;

import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.config.LobsterConfig;
import com.gzzm.lobster.config.SystemConfigEntry;
import com.gzzm.lobster.config.SystemConfigEntryDao;
import com.gzzm.lobster.identity.AdminGuard;
import com.gzzm.lobster.identity.UserContext;
import com.gzzm.lobster.identity.UserContextHolder;
import net.cyan.arachne.HttpMethod;
import net.cyan.arachne.annotation.Service;
import net.cyan.nest.annotation.Inject;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class AdminSystemConfigApi {

    private static final String API_CENTER_CONSOLE_BASE_URL = "apiCenterConsoleBaseUrl";

    @Inject private SystemConfigEntryDao configDao;

    @Service(url = "/ai/api/admin/system-config", method = HttpMethod.all)
    public Map<String, Object> list() throws Exception {
        AdminGuard.requireAdmin();
        return response();
    }

    @Service(url = "/ai/api/admin/system-config/api-center-console-base-url", method = HttpMethod.post)
    public Map<String, Object> updateApiCenterConsoleBaseUrl(String apiCenterConsoleBaseUrl) throws Exception {
        AdminGuard.requireAdmin();
        String value = normalizeUrl(apiCenterConsoleBaseUrl);
        Date now = new Date();
        SystemConfigEntry row = configDao.getByKey(API_CENTER_CONSOLE_BASE_URL);
        if (row == null) {
            row = new SystemConfigEntry();
            row.setConfigKey(API_CENTER_CONSOLE_BASE_URL);
            row.setCreateTime(now);
        }
        row.setConfigValue(value);
        row.setValueType("string");
        UserContext user = UserContextHolder.get();
        row.setUpdatedBy(user == null ? "" : user.getUserId());
        row.setUpdateTime(now);
        configDao.save(row);
        new LobsterConfig().setApiCenterConsoleBaseUrl(value);
        return response();
    }

    private String normalizeUrl(String raw) {
        String value = raw == null ? "" : raw.trim();
        if (value.isEmpty()) return "";
        String lower = value.toLowerCase();
        if (!lower.startsWith("http://") && !lower.startsWith("https://")) {
            throw new LobsterException("admin.system_config.invalid_api_center_url",
                    "API Center console URL must start with http:// or https://");
        }
        return value.replaceAll("/+$", "");
    }

    private Map<String, Object> response() throws Exception {
        List<SystemConfigEntry> dbRows = configDao.listAll();
        if (dbRows == null) dbRows = new ArrayList<>();
        Map<String, SystemConfigEntry> dbByKey = new LinkedHashMap<>();
        for (SystemConfigEntry row : dbRows) {
            if (row != null && row.getConfigKey() != null) dbByKey.put(row.getConfigKey(), row);
        }

        SystemConfigEntry apiCenterRow = dbByKey.get(API_CENTER_CONSOLE_BASE_URL);
        String apiCenterValue = apiCenterRow == null
                ? LobsterConfig.getApiCenterConsoleBaseUrl()
                : nullToEmpty(apiCenterRow.getConfigValue());
        if (!apiCenterValue.equals(LobsterConfig.getApiCenterConsoleBaseUrl())) {
            new LobsterConfig().setApiCenterConsoleBaseUrl(apiCenterValue);
        }

        List<Map<String, Object>> items = new ArrayList<>();
        Map<String, Boolean> emitted = new LinkedHashMap<>();
        for (Method m : LobsterConfig.class.getMethods()) {
            if (!Modifier.isStatic(m.getModifiers())) continue;
            if (m.getParameterTypes().length != 0) continue;
            String name = m.getName();
            String key = configKey(name);
            if (key == null) continue;
            Object raw = m.invoke(null);
            String source = "lobster.xml/default";
            if (API_CENTER_CONSOLE_BASE_URL.equals(key)) {
                raw = apiCenterValue;
                source = apiCenterRow == null ? source : "database";
            }
            Map<String, Object> row = item(key, raw, valueType(raw), source,
                    API_CENTER_CONSOLE_BASE_URL.equals(key), category(key), description(key));
            if (apiCenterRow != null && API_CENTER_CONSOLE_BASE_URL.equals(key)) {
                row.put("updatedBy", nullToEmpty(apiCenterRow.getUpdatedBy()));
                row.put("updateTime", apiCenterRow.getUpdateTime());
            }
            items.add(row);
            emitted.put(key, Boolean.TRUE);
        }
        for (SystemConfigEntry dbRow : dbRows) {
            if (dbRow == null || dbRow.getConfigKey() == null || emitted.containsKey(dbRow.getConfigKey())) continue;
            Map<String, Object> row = item(dbRow.getConfigKey(), dbRow.getConfigValue(),
                    dbRow.getValueType() == null || dbRow.getValueType().trim().isEmpty()
                            ? "string" : dbRow.getValueType(),
                    "database", false, category(dbRow.getConfigKey()), "AI_SYSTEM_CONFIG 持久化系统配置，只读展示。");
            row.put("updatedBy", nullToEmpty(dbRow.getUpdatedBy()));
            row.put("updateTime", dbRow.getUpdateTime());
            items.add(row);
        }
        items.sort(Comparator
                .comparing((Map<String, Object> m) -> Boolean.TRUE.equals(m.get("editable")) ? 0 : 1)
                .thenComparing(m -> String.valueOf(m.get("category")))
                .thenComparing(m -> String.valueOf(m.get("key"))));

        Map<String, Object> out = new LinkedHashMap<>();
        out.put("items", items);
        out.put(API_CENTER_CONSOLE_BASE_URL, apiCenterValue);
        return out;
    }

    private Map<String, Object> item(String key, Object raw, String type, String source,
                                     boolean editable, String category, String description) {
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("key", key);
        out.put("value", displayValue(key, raw));
        out.put("valueType", type);
        out.put("category", category);
        out.put("editable", Boolean.valueOf(editable));
        out.put("source", source);
        out.put("description", description);
        return out;
    }

    private static String configKey(String methodName) {
        if (methodName == null || "getClass".equals(methodName)) return null;
        String stem;
        if (methodName.startsWith("get") && methodName.length() > 3) stem = methodName.substring(3);
        else if (methodName.startsWith("is") && methodName.length() > 2) stem = methodName.substring(2);
        else return null;
        return Character.toLowerCase(stem.charAt(0)) + stem.substring(1);
    }

    private static String valueType(Object value) {
        if (value instanceof Boolean) return "boolean";
        if (value instanceof Number) return "number";
        return "string";
    }

    private static String displayValue(String key, Object value) {
        if (value == null) return "";
        String text = String.valueOf(value);
        String lower = key == null ? "" : key.toLowerCase();
        if (lower.endsWith("apikey") || lower.contains("token") || lower.contains("secret")
                || lower.contains("password")) {
            if (text.isEmpty()) return "";
            int tail = Math.max(0, Math.min(LobsterConfig.getApiKeyMaskTailLen(), text.length()));
            return "******" + text.substring(text.length() - tail);
        }
        return text;
    }

    private static String category(String key) {
        if (key.startsWith("apiCenter") || key.startsWith("admin")) return "Admin";
        if (key.startsWith("llm") || key.startsWith("summarizer") || key.startsWith("defaultContext")
                || key.startsWith("maxTurns") || key.startsWith("perUser") || key.startsWith("perThread")) return "Agent";
        if (key.startsWith("memory")) return "Memory";
        if (key.startsWith("mcp")) return "MCP";
        if (key.startsWith("tool")) return "Tool";
        if (key.startsWith("oaKnowledge") || key.startsWith("kb")) return "Knowledge Base";
        if (key.startsWith("upload") || key.startsWith("parsed") || key.startsWith("xlsx")
                || key.startsWith("outline") || key.startsWith("pdf") || key.startsWith("contentStore")) return "Storage";
        if (key.startsWith("sandbox") || key.startsWith("systemSandbox") || key.startsWith("systemGuidance")) return "Sandbox";
        return "Other";
    }

    private static String description(String key) {
        if (API_CENTER_CONSOLE_BASE_URL.equals(key)) return "API Center 管理后台基础地址，用于后台菜单和 MCP 源服务跳转。";
        return "LobsterConfig 当前运行值，只读展示。";
    }

    private static String nullToEmpty(String value) {
        return value == null ? "" : value;
    }
}
