package top.r3944realms.ltdmanager.core.config

import org.yaml.snakeyaml.LoaderOptions
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
import org.yaml.snakeyaml.error.YAMLException
import org.yaml.snakeyaml.introspector.Property
import org.yaml.snakeyaml.introspector.PropertyUtils
import top.r3944realms.ltdmanager.utils.ConfigInitializer
import top.r3944realms.ltdmanager.utils.LoggerUtil
import top.r3944realms.ltdmanager.utils.NamingConventionUtil
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

object YamlConfigLoader {
    val appConfigFilePath: Path = Paths.get("config/application.yaml")
    val moduleConfigFilePath: Path = Paths.get("config/module.yaml")
    private val _app_config by lazy { loadAppConfigWrapper() }
    val appConfig: AppConfigWrapper get() = _app_config
    private val _module_config by lazy { loadModuleConfigWrapper() }
    val moduleConfig: ModuleConfigWrapper get() = _module_config

    init {
        ConfigInitializer.initConfig("module.yaml", "config", false)
        ConfigInitializer.initConfig("application.yaml", "config")
        ConfigInitializer.initSqlTemplates()
        runCatching {
            ensureConfigEncrypted(_app_config)
        }.onFailure { e ->
            LoggerUtil.syncError("配置加密失败: ${e.message?.lineSequence()?.firstOrNull() ?: e.message}")
        }
    }

    private fun ensureConfigEncrypted(config: AppConfigWrapper?) {
        config?.database?.encryptPassword()
        config?.websocket?.encryptToken()
        config?.http?.encryptToken()
        config?.mcsm?.encryptApi()
        config?.mail?.encryptPassword()
        config?.tools?.encryptPassword()
        config?.blessingSkinServer?.invitationApi?.encryptToken()
        config?.dgLab?.wsServer?.encryptPassword()
        config?.imgTu?.encryptPassword()
        config?.whitelistSystem?.encryptToken()
    }

    private fun loadAppConfigWrapper(): AppConfigWrapper {
        if (!Files.exists(appConfigFilePath)) {
            throw RuntimeException("应用配置文件未找到: $appConfigFilePath")
        }
        return loadYaml(appConfigFilePath, AppConfigWrapper::class.java)
    }

    private fun loadModuleConfigWrapper(): ModuleConfigWrapper {
        if (!Files.exists(moduleConfigFilePath)) {
            throw RuntimeException("模块配置文件未找到: $moduleConfigFilePath")
        }
        return loadYaml(moduleConfigFilePath, ModuleConfigWrapper::class.java)
    }

    private fun <T> loadYaml(filePath: Path, clazz: Class<T>): T {
        val yamlContent = Files.readString(filePath)
        return try {
            Yaml(getConstructor(clazz)).load(yamlContent)
                ?: throw RuntimeException("YAML 解析返回 null: $filePath")
        } catch (e: ConfigParseException) {
            throw e
        } catch (e: Exception) {
            throw ConfigParseException(formatYamlError(e, filePath), e)
        }
    }

    /** 提取 SnakeYAML 异常中的关键信息并格式化为可读的错误 */
    private fun formatYamlError(e: Throwable, filePath: Path): String {
        val sb = StringBuilder()
        sb.appendLine("配置文件解析失败: $filePath")
        sb.appendLine("─".repeat(48))

        // 提取所有嵌套的 YAMLException 信息 (逆序，最深层优先)
        val causes = generateSequence(e) { it.cause }.toList().reversed()
        for (cause in causes) {
            val msg = cause.message ?: continue
            // 只保留以 YAML 行号/列号开头的错误和 "Unable to find" 错误
            if (msg.contains("Unable to find") || msg.startsWith("Cannot create") ||
                msg.contains("in 'string', line") || msg.contains("Unable to find enum")
            ) {
                // 提取行号信息
                val lineMatch = Regex("line (\\d+)").find(msg)
                val lineInfo = lineMatch?.let { " (第${it.groupValues[1]}行)" } ?: ""
                // 提取关键描述
                val desc = when {
                    msg.contains("Unable to find property") -> {
                        val prop = Regex("property '(\\S+)'").find(msg)?.groupValues?.get(1) ?: "?"
                        "未知配置项: '$prop'$lineInfo"
                    }
                    msg.contains("Unable to find enum value") -> {
                        val value = Regex("enum value '(\\S+)'").find(msg)?.groupValues?.get(1) ?: "?"
                        val enumClass = Regex("enum class: (\\S+)").find(msg)?.groupValues?.get(1)?.substringAfterLast('.') ?: ""
                        "未知枚举值: '$value' (有效值见 $enumClass 定义)$lineInfo"
                    }
                    else -> msg.lines().first().trim()
                }
                sb.appendLine("  • $desc")
            }
        }

        if (sb.lines().count() <= 3) {
            // 没有提取到结构化信息，回退到原始摘要
            sb.appendLine("  详情: ${e.message?.lineSequence()?.firstOrNull()?.take(120) ?: e.javaClass.simpleName}")
        }
        return sb.toString().trimEnd()
    }

    private fun getConstructor(clazz: Class<*>): Constructor {
        val propertyUtils = object : PropertyUtils() {
            override fun getProperty(type: Class<*>, name: String): Property {
                val processedName = if (name.contains("-")) {
                    NamingConventionUtil.hyphenToCamel(name)
                } else {
                    name
                }
                return try {
                    super.getProperty(type, processedName)
                } catch (e: YAMLException) {
                    // 阻止 SnakeYAML 在错误信息中 dump 完整 JavaBean
                    throw ConfigParseException(
                        "未知配置项 '$name' (映射为 '$processedName')，类型 '${type.simpleName}' 中无此字段"
                    )
                }
            }
        }

        return Constructor(clazz, LoaderOptions()).apply {
            setPropertyUtils(propertyUtils)
        }
    }

    fun loadDatabaseConfig(): DatabaseConfig = appConfig.database
    fun loadCryptoConfig(): CryptoConfig = appConfig.crypto
    fun loadMcsmConfig(): McsmConfig = appConfig.mcsm
    fun loadWebsocketConfig(): WebsocketConfig = appConfig.websocket
    fun loadHttpConfig(): HttpConfig = appConfig.http
    fun loadModeConfig(): ModeConfig = appConfig.mode
    fun loadToolConfig(): ToolConfig = appConfig.tools
    fun loadMailConfig(): MailConfig = appConfig.mail
    fun loadBlessingSkinServerConfig(): BlessingSkinServerConfig = appConfig.blessingSkinServer
    fun loadDgLabConfig(): DgLabConfig = appConfig.dgLab
    fun loadTuImgConfig(): ImgTuConfig = appConfig.imgTu
    fun loadWhitelistSystemConfig(): WhitelistSystemConfig = appConfig.whitelistSystem
    fun loadModuleConfig(): ModuleConfig = moduleConfig.module

    data class AppConfigWrapper(
        var database: DatabaseConfig = DatabaseConfig(),
        var crypto: CryptoConfig = CryptoConfig(),
        var mode: ModeConfig = ModeConfig(),
        var websocket: WebsocketConfig = WebsocketConfig(),
        var http: HttpConfig = HttpConfig(),
        var tools: ToolConfig = ToolConfig(),
        var mail: MailConfig = MailConfig(),
        var mcsm: McsmConfig = McsmConfig(),
        var blessingSkinServer: BlessingSkinServerConfig = BlessingSkinServerConfig(),
        var dgLab: DgLabConfig = DgLabConfig(),
        var imgTu: ImgTuConfig = ImgTuConfig(),
        var whitelistSystem: WhitelistSystemConfig = WhitelistSystemConfig(),
    ) {
        override fun toString(): String =
            "AppConfig(loaded=${database.url != null})"
    }

    data class ModuleConfigWrapper(
        var module: ModuleConfig = ModuleConfig(),
    )
}

/** 配置解析专用异常 — 不递归打印 cause 避免海量堆栈 */
class ConfigParseException(message: String, cause: Throwable? = null) :
    RuntimeException(message, cause, true, false)
