import fs from 'fs/promises';
import fsSync from 'fs';
import path from 'path';
import YAML from 'yaml';
import PropertiesReader from 'properties-reader';
import TOML from 'smol-toml';
import logger from '../../utils/logger.js';
export class GameConfigManager {
    constructor() {
        this.configsCache = null;
        const baseDir = process.cwd();
        const possiblePaths = [
            path.join(baseDir, 'data', 'gameconfig'),
            path.join(baseDir, 'server', 'data', 'gameconfig'),
            path.join(baseDir, '..', 'server', 'data', 'gameconfig'),
        ];
        this.configSchemasDir = possiblePaths[0];
        for (const possiblePath of possiblePaths) {
            try {
                fsSync.accessSync(possiblePath);
                this.configSchemasDir = possiblePath;
                break;
            }
            catch {
            }
        }
        logger.info(`GameConfigManager 配置目录: ${this.configSchemasDir}`);
        this.supportedParsers = new Map([
            ['properties', this.parseWithProperties.bind(this)],
            ['configobj', this.parseWithConfigObj.bind(this)],
            ['yaml', this.parseWithYaml.bind(this)],
            ['ruamel.yaml', this.parseWithYaml.bind(this)],
            ['json', this.parseWithJson.bind(this)],
            ['toml', this.parseWithToml.bind(this)]
        ]);
    }
    async getAvailableGameConfigs() {
        if (this.configsCache) {
            return this.configsCache;
        }
        try {
            const files = await fs.readdir(this.configSchemasDir);
            const ymlFiles = files.filter(file => file.endsWith('.yml') || file.endsWith('.yaml'));
            const configs = [];
            const loadedGames = [];
            const failedFiles = [];
            for (const file of ymlFiles) {
                try {
                    const filePath = path.join(this.configSchemasDir, file);
                    const content = await fs.readFile(filePath, 'utf-8');
                    const schema = YAML.parse(content);
                    if (schema && schema.meta && schema.sections) {
                        configs.push(schema);
                        loadedGames.push(schema.meta.game_name);
                    }
                    else {
                        failedFiles.push(file);
                        logger.warn(`配置文件格式不正确: ${file}`);
                    }
                }
                catch (error) {
                    failedFiles.push(file);
                    logger.warn(`解析配置模板文件失败: ${file}`, error);
                }
            }
            if (configs.length > 0) {
                logger.info(`成功加载 ${configs.length} 个游戏配置模板: ${loadedGames.join(', ')}`);
            }
            if (failedFiles.length > 0) {
                logger.warn(`${failedFiles.length} 个配置文件加载失败: ${failedFiles.join(', ')}`);
            }
            this.configsCache = configs;
            return configs;
        }
        catch (error) {
            logger.error('获取游戏配置模板失败:', error);
            throw new Error('获取游戏配置模板失败');
        }
    }
    async getGameConfigSchema(gameName) {
        try {
            const configs = await this.getAvailableGameConfigs();
            return configs.find(config => config.meta.game_name === gameName) || null;
        }
        catch (error) {
            logger.error(`获取游戏配置模板失败: ${gameName}`, error);
            return null;
        }
    }
    async readGameConfig(serverPath, configSchema) {
        try {
            const configFilePath = configSchema.meta.config_file;
            const fullConfigPath = path.join(serverPath, configFilePath);
            const parserType = configSchema.meta.parser || 'configobj';
            logger.debug(`正在读取配置文件: ${fullConfigPath}`);
            logger.debug(`使用解析器: ${parserType}`);
            try {
                await fs.access(fullConfigPath);
            }
            catch {
                throw new Error(`配置文件不存在: ${fullConfigPath}`);
            }
            const parser = this.supportedParsers.get(parserType);
            if (!parser) {
                throw new Error(`不支持的解析器类型: ${parserType}`);
            }
            return await parser(fullConfigPath, configSchema);
        }
        catch (error) {
            logger.error('读取游戏配置文件失败:', error);
            throw error;
        }
    }
    async saveGameConfig(serverPath, configSchema, configData) {
        try {
            const configFilePath = configSchema.meta.config_file;
            const fullConfigPath = path.join(serverPath, configFilePath);
            const parserType = configSchema.meta.parser || 'configobj';
            logger.debug(`正在保存配置文件: ${fullConfigPath}`);
            logger.debug(`使用解析器: ${parserType}`);
            const configDir = path.dirname(fullConfigPath);
            await fs.mkdir(configDir, { recursive: true });
            const mergedConfig = await this.mergeConfigWithExistingFile(fullConfigPath, configData, configSchema, parserType);
            switch (parserType) {
                case 'properties':
                    await this.saveWithProperties(fullConfigPath, mergedConfig, configSchema);
                    break;
                case 'configobj':
                    await this.saveWithConfigObj(fullConfigPath, mergedConfig, configSchema);
                    break;
                case 'yaml':
                case 'ruamel.yaml':
                    await this.saveWithYaml(fullConfigPath, mergedConfig, configSchema);
                    break;
                case 'json':
                    await this.saveWithJson(fullConfigPath, mergedConfig, configSchema);
                    break;
                case 'toml':
                    await this.saveWithToml(fullConfigPath, mergedConfig, configSchema);
                    break;
                default:
                    throw new Error(`不支持的解析器类型: ${parserType}`);
            }
            logger.info(`配置文件保存成功: ${fullConfigPath}`, { service: 'gsm3-server' });
            return true;
        }
        catch (error) {
            logger.error('保存游戏配置文件失败:', error);
            return false;
        }
    }
    getDefaultValues(configSchema) {
        const result = {};
        for (const section of configSchema.sections) {
            result[section.key] = {};
            for (const field of section.fields) {
                if (field.type === 'nested' && field.nested_fields) {
                    const nestedValues = {};
                    for (const nestedField of field.nested_fields) {
                        nestedValues[nestedField.name] = nestedField.default;
                    }
                    result[section.key][field.name] = nestedValues;
                }
                else {
                    result[section.key][field.name] = field.default;
                }
            }
        }
        return result;
    }
    async parseWithProperties(configPath, configSchema) {
        try {
            const properties = PropertiesReader(configPath);
            const result = {};
            for (const section of configSchema.sections) {
                result[section.key] = {};
                for (const field of section.fields) {
                    const value = properties.get(field.name);
                    if (value !== null) {
                        result[section.key][field.name] = this.convertValue(value, field.type);
                    }
                    else {
                        result[section.key][field.name] = field.default;
                    }
                }
            }
            return result;
        }
        catch (error) {
            logger.error('Properties解析失败:', error);
            throw error;
        }
    }
    async parseWithConfigObj(configPath, configSchema) {
        try {
            const content = await fs.readFile(configPath, 'utf-8');
            const result = {};
            const lines = content.split('\n');
            let currentSection = '';
            const parsedData = {};
            for (const line of lines) {
                const trimmedLine = line.trim();
                if (!trimmedLine || trimmedLine.startsWith('#') || trimmedLine.startsWith(';')) {
                    continue;
                }
                const sectionMatch = trimmedLine.match(/^\[(.+)\]$/);
                if (sectionMatch) {
                    currentSection = sectionMatch[1];
                    if (!parsedData[currentSection]) {
                        parsedData[currentSection] = {};
                    }
                    continue;
                }
                const keyValueMatch = trimmedLine.match(/^([^=]+)=(.*)$/);
                if (keyValueMatch && currentSection) {
                    const key = keyValueMatch[1].trim();
                    const value = keyValueMatch[2].trim();
                    parsedData[currentSection][key] = value;
                }
            }
            for (const section of configSchema.sections) {
                result[section.key] = {};
                const sectionData = parsedData[section.key] || {};
                for (const field of section.fields) {
                    if (field.type === 'nested' && field.nested_fields) {
                        const nestedValue = sectionData[field.name] || field.default;
                        result[section.key][field.name] = this.parseNestedValue(nestedValue, field.nested_fields);
                    }
                    else {
                        const value = sectionData[field.name];
                        if (value !== undefined) {
                            result[section.key][field.name] = this.convertValue(value, field.type);
                        }
                        else {
                            result[section.key][field.name] = field.default;
                        }
                    }
                }
            }
            return result;
        }
        catch (error) {
            logger.error('ConfigObj解析失败:', error);
            throw error;
        }
    }
    async parseWithYaml(configPath, configSchema) {
        try {
            const content = await fs.readFile(configPath, 'utf-8');
            const yamlData = YAML.parse(content) || {};
            const result = {};
            for (const section of configSchema.sections) {
                result[section.key] = {};
                const sectionData = yamlData[section.key] || {};
                for (const field of section.fields) {
                    const value = sectionData[field.name];
                    if (value !== undefined) {
                        result[section.key][field.name] = value;
                    }
                    else {
                        result[section.key][field.name] = field.default;
                    }
                }
            }
            return result;
        }
        catch (error) {
            logger.error('YAML解析失败:', error);
            throw error;
        }
    }
    async parseWithJson(configPath, configSchema) {
        try {
            const content = await fs.readFile(configPath, 'utf-8');
            const jsonData = JSON.parse(content);
            const result = {};
            for (const section of configSchema.sections) {
                result[section.key] = {};
                const sectionData = section.key === '' ? jsonData : (jsonData[section.key] || {});
                for (const field of section.fields) {
                    if (field.type === 'nested' && field.nested_fields) {
                        const nestedValue = sectionData[field.name];
                        if (nestedValue !== undefined && typeof nestedValue === 'object') {
                            result[section.key][field.name] = nestedValue;
                        }
                        else {
                            const nestedDefaults = {};
                            for (const nestedField of field.nested_fields) {
                                nestedDefaults[nestedField.name] = nestedField.default;
                            }
                            result[section.key][field.name] = nestedDefaults;
                        }
                    }
                    else {
                        const value = sectionData[field.name];
                        if (value !== undefined) {
                            result[section.key][field.name] = value;
                        }
                        else {
                            result[section.key][field.name] = field.default;
                        }
                    }
                }
            }
            return result;
        }
        catch (error) {
            logger.error('JSON解析失败:', error);
            throw error;
        }
    }
    parseNestedValue(value, nestedFields) {
        const result = {};
        if (typeof value === 'string') {
            const cleanValue = value.replace(/^\(|\)$/g, '');
            const pairs = cleanValue.split(',');
            for (const pair of pairs) {
                const [key, val] = pair.split('=').map(s => s.trim());
                if (key && val !== undefined) {
                    const field = nestedFields.find(f => f.name === key);
                    if (field) {
                        result[key] = this.convertValue(val, field.type);
                    }
                }
            }
        }
        for (const field of nestedFields) {
            if (!(field.name in result)) {
                result[field.name] = field.default;
            }
        }
        return result;
    }
    convertValue(value, type) {
        if (value === null || value === undefined) {
            return value;
        }
        const strValue = String(value);
        switch (type) {
            case 'number':
                const num = Number(strValue);
                return isNaN(num) ? 0 : num;
            case 'boolean':
                return strValue.toLowerCase() === 'true' || strValue === '1';
            case 'string':
            default:
                return strValue;
        }
    }
    async mergeConfigWithExistingFile(configPath, configData, configSchema, parserType) {
        const existingConfig = await this.readRawConfig(configPath, parserType);
        switch (parserType) {
            case 'properties':
                return this.mergePropertiesConfig(existingConfig, configData, configSchema);
            case 'configobj':
                return this.mergeConfigObjConfig(existingConfig, configData, configSchema);
            case 'yaml':
            case 'ruamel.yaml':
            case 'json':
            case 'toml':
                return this.mergeStructuredConfig(existingConfig, configData, configSchema);
            default:
                throw new Error(`不支持的解析器类型: ${parserType}`);
        }
    }
    async readRawConfig(configPath, parserType) {
        try {
            await fs.access(configPath);
        }
        catch {
            return {};
        }
        switch (parserType) {
            case 'properties':
                return await this.parseRawProperties(configPath);
            case 'configobj':
                return await this.parseRawConfigObj(configPath);
            case 'yaml':
            case 'ruamel.yaml':
                return await this.parseRawYaml(configPath);
            case 'json':
                return await this.parseRawJson(configPath);
            case 'toml':
                return await this.parseRawToml(configPath);
            default:
                return {};
        }
    }
    async parseRawProperties(configPath) {
        const properties = PropertiesReader(configPath);
        const rawData = properties.getAllProperties() || {};
        return this.cloneRawObject(rawData);
    }
    async parseRawConfigObj(configPath) {
        const content = await fs.readFile(configPath, 'utf-8');
        const lines = content.split(/\r?\n/);
        const parsedData = {};
        let currentSection = '';
        for (const line of lines) {
            const trimmedLine = line.trim();
            if (!trimmedLine || trimmedLine.startsWith('#') || trimmedLine.startsWith(';')) {
                continue;
            }
            const sectionMatch = trimmedLine.match(/^\[(.+)\]$/);
            if (sectionMatch) {
                currentSection = sectionMatch[1];
                if (!this.isPlainObject(parsedData[currentSection])) {
                    parsedData[currentSection] = {};
                }
                continue;
            }
            const keyValueMatch = trimmedLine.match(/^([^=]+)=(.*)$/);
            if (keyValueMatch && currentSection) {
                const key = keyValueMatch[1].trim();
                const value = keyValueMatch[2].trim();
                const sectionData = this.ensureObjectContainer(parsedData, currentSection);
                sectionData[key] = value;
            }
        }
        return parsedData;
    }
    async parseRawYaml(configPath) {
        const content = await fs.readFile(configPath, 'utf-8');
        const rawData = YAML.parse(content) || {};
        return this.cloneRawObject(rawData);
    }
    async parseRawJson(configPath) {
        const content = await fs.readFile(configPath, 'utf-8');
        const rawData = JSON.parse(content);
        return this.cloneRawObject(rawData || {});
    }
    async parseRawToml(configPath) {
        const content = await fs.readFile(configPath, 'utf-8');
        const rawData = TOML.parse(content) || {};
        return this.cloneRawObject(rawData);
    }
    mergePropertiesConfig(existingConfig, configData, configSchema) {
        const mergedConfig = this.cloneRawObject(existingConfig);
        for (const section of configSchema.sections) {
            const sectionData = configData[section.key] || {};
            for (const field of section.fields) {
                const value = sectionData[field.name];
                if (value === undefined) {
                    continue;
                }
                mergedConfig[field.name] = field.type === 'nested' && field.nested_fields
                    ? this.formatNestedValue(value, field.nested_fields, existingConfig[field.name])
                    : this.normalizeScalarValue(value, field.type);
            }
        }
        return mergedConfig;
    }
    mergeConfigObjConfig(existingConfig, configData, configSchema) {
        const mergedConfig = this.cloneRawObject(existingConfig);
        for (const section of configSchema.sections) {
            const sectionData = configData[section.key] || {};
            const targetSection = this.ensureObjectContainer(mergedConfig, section.key);
            const existingSection = this.isPlainObject(existingConfig[section.key]) ? existingConfig[section.key] : {};
            for (const field of section.fields) {
                const value = sectionData[field.name];
                if (value === undefined) {
                    continue;
                }
                if (field.type === 'nested' && field.nested_fields) {
                    targetSection[field.name] = this.formatNestedValue(value, field.nested_fields, existingSection[field.name]);
                }
                else {
                    targetSection[field.name] = this.normalizeScalarValue(value, field.type);
                }
            }
        }
        return mergedConfig;
    }
    mergeStructuredConfig(existingConfig, configData, configSchema) {
        const mergedConfig = this.cloneRawObject(existingConfig);
        for (const section of configSchema.sections) {
            const sectionData = configData[section.key] || {};
            const targetContainer = section.key === ''
                ? mergedConfig
                : this.ensureObjectContainer(mergedConfig, section.key);
            const existingContainer = section.key === ''
                ? existingConfig
                : (this.isPlainObject(existingConfig[section.key]) ? existingConfig[section.key] : {});
            for (const field of section.fields) {
                const value = sectionData[field.name];
                if (value === undefined) {
                    continue;
                }
                if (field.type === 'nested' && field.nested_fields && this.isPlainObject(value)) {
                    const existingNested = this.isPlainObject(existingContainer[field.name])
                        ? existingContainer[field.name]
                        : {};
                    targetContainer[field.name] = {
                        ...this.cloneRawObject(existingNested),
                        ...value
                    };
                }
                else {
                    targetContainer[field.name] = value;
                }
            }
        }
        return mergedConfig;
    }
    isPlainObject(value) {
        return typeof value === 'object' && value !== null && !Array.isArray(value);
    }
    ensureObjectContainer(target, key) {
        if (!this.isPlainObject(target[key])) {
            target[key] = {};
        }
        return target[key];
    }
    cloneRawObject(value) {
        if (value === null || value === undefined) {
            return value;
        }
        return structuredClone(value);
    }
    normalizeScalarValue(value, type) {
        switch (type) {
            case 'number':
                return Number(value);
            case 'boolean':
                return Boolean(value);
            case 'string':
            case 'select':
            default:
                return String(value);
        }
    }
    parseLooseNestedPairs(value) {
        const result = {};
        if (typeof value !== 'string') {
            return result;
        }
        const cleanValue = value.replace(/^\(|\)$/g, '');
        if (!cleanValue) {
            return result;
        }
        const pairs = cleanValue.split(',');
        for (const pair of pairs) {
            const equalIndex = pair.indexOf('=');
            if (equalIndex === -1) {
                continue;
            }
            const key = pair.slice(0, equalIndex).trim();
            const fieldValue = pair.slice(equalIndex + 1).trim();
            if (key) {
                result[key] = fieldValue;
            }
        }
        return result;
    }
    async saveWithProperties(configPath, configData, configSchema) {
        const lines = [];
        for (const [key, value] of Object.entries(configData)) {
            if (value !== undefined && !this.isPlainObject(value)) {
                lines.push(`${key}=${value}`);
            }
        }
        await fs.writeFile(configPath, lines.join('\n'), 'utf-8');
    }
    async saveWithConfigObj(configPath, configData, configSchema) {
        const lines = [];
        for (const [sectionKey, sectionValue] of Object.entries(configData)) {
            if (!this.isPlainObject(sectionValue)) {
                continue;
            }
            lines.push(`[${sectionKey}]`);
            for (const [fieldName, fieldValue] of Object.entries(sectionValue)) {
                if (fieldValue !== undefined) {
                    lines.push(`${fieldName}=${fieldValue}`);
                }
            }
            lines.push('');
        }
        await fs.writeFile(configPath, lines.join('\n'), 'utf-8');
    }
    async saveWithYaml(configPath, configData, configSchema) {
        const yamlContent = YAML.stringify(configData);
        await fs.writeFile(configPath, yamlContent, 'utf-8');
    }
    async saveWithJson(configPath, configData, configSchema) {
        const isFlat = configSchema.sections.length === 1 && configSchema.sections[0].key === '';
        let jsonData;
        if (isFlat) {
            jsonData = configData;
        }
        else {
            jsonData = configData;
        }
        const jsonContent = JSON.stringify(jsonData, null, 2);
        await fs.writeFile(configPath, jsonContent, 'utf-8');
    }
    formatNestedValue(value, nestedFields, existingValue) {
        if (typeof value === 'object' && value !== null) {
            const existingPairs = this.parseLooseNestedPairs(existingValue);
            const mergedPairs = {
                ...existingPairs,
                ...value
            };
            const pairs = [];
            const schemaFieldNames = new Set(nestedFields.map(field => field.name));
            for (const field of nestedFields) {
                const fieldValue = mergedPairs[field.name];
                if (fieldValue !== undefined) {
                    pairs.push(`${field.name}=${fieldValue}`);
                }
            }
            for (const [key, fieldValue] of Object.entries(mergedPairs)) {
                if (!schemaFieldNames.has(key) && fieldValue !== undefined) {
                    pairs.push(`${key}=${fieldValue}`);
                }
            }
            return `(${pairs.join(',')})`;
        }
        return String(value);
    }
    async parseWithToml(configPath, configSchema) {
        try {
            const content = await fs.readFile(configPath, 'utf-8');
            const tomlData = TOML.parse(content) || {};
            const result = {};
            for (const section of configSchema.sections) {
                result[section.key] = {};
                const sectionData = tomlData[section.key] || {};
                for (const field of section.fields) {
                    const value = sectionData[field.name];
                    if (value !== undefined) {
                        result[section.key][field.name] = value;
                    }
                    else {
                        result[section.key][field.name] = field.default;
                    }
                }
            }
            return result;
        }
        catch (error) {
            logger.error('TOML解析失败:', error);
            throw error;
        }
    }
    async saveWithToml(configPath, configData, configSchema) {
        try {
            const tomlContent = TOML.stringify(configData);
            await fs.writeFile(configPath, tomlContent, 'utf-8');
        }
        catch (error) {
            logger.error('TOML保存失败:', error);
            throw error;
        }
    }
}
