"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfig = loadConfig;
const fs_1 = require("fs");
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const toml_1 = require("@iarna/toml");
const zod_1 = require("zod");
const constants_1 = require("../constants");
const mcpConfigSchema = zod_1.z
    .object({
    enabled: zod_1.z.boolean().optional(),
    merge_strategy: zod_1.z.enum(['merge', 'overwrite']).optional(),
})
    .optional();
const agentConfigSchema = zod_1.z
    .object({
    enabled: zod_1.z.boolean().optional(),
    output_path: zod_1.z.string().optional(),
    output_path_instructions: zod_1.z.string().optional(),
    output_path_config: zod_1.z.string().optional(),
    mcp: mcpConfigSchema,
})
    .optional();
const rulerConfigSchema = zod_1.z.object({
    default_agents: zod_1.z.array(zod_1.z.string()).optional(),
    agents: zod_1.z.record(zod_1.z.string(), agentConfigSchema).optional(),
    mcp: zod_1.z
        .object({
        enabled: zod_1.z.boolean().optional(),
        merge_strategy: zod_1.z.enum(['merge', 'overwrite']).optional(),
    })
        .optional(),
    gitignore: zod_1.z
        .object({
        enabled: zod_1.z.boolean().optional(),
        local: zod_1.z.boolean().optional(),
    })
        .optional(),
    skills: zod_1.z
        .object({
        enabled: zod_1.z.boolean().optional(),
    })
        .optional(),
    nested: zod_1.z.boolean().optional(),
});
/**
 * Recursively creates a new object with only enumerable string keys,
 * effectively excluding Symbol properties.
 * The @iarna/toml parser adds Symbol properties (Symbol(type), Symbol(declared))
 * for metadata, which Zod v4+ validates and rejects as invalid record keys.
 * By rebuilding the object structure using Object.keys(), we create clean objects
 * that only contain the actual data without Symbol metadata.
 */
function stripSymbols(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map(stripSymbols);
    }
    const result = {};
    for (const key of Object.keys(obj)) {
        result[key] = stripSymbols(obj[key]);
    }
    return result;
}
/**
 * Loads and parses the ruler TOML configuration file, applying defaults.
 * If the file is missing or invalid, returns empty/default config.
 */
async function loadConfig(options) {
    const { projectRoot, configPath, cliAgents } = options;
    let configFile;
    if (configPath) {
        configFile = path.resolve(configPath);
    }
    else {
        // Try local .ruler/ruler.toml first
        const localConfigFile = path.join(projectRoot, '.ruler', 'ruler.toml');
        try {
            await fs_1.promises.access(localConfigFile);
            configFile = localConfigFile;
        }
        catch {
            // If local config doesn't exist, try global config
            const xdgConfigDir = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
            configFile = path.join(xdgConfigDir, 'ruler', 'ruler.toml');
        }
    }
    let raw = {};
    try {
        const text = await fs_1.promises.readFile(configFile, 'utf8');
        const parsed = text.trim() ? (0, toml_1.parse)(text) : {};
        // Strip Symbol properties added by @iarna/toml (required for Zod v4+)
        raw = stripSymbols(parsed);
        // Validate the configuration with zod
        const validationResult = rulerConfigSchema.safeParse(raw);
        if (!validationResult.success) {
            throw (0, constants_1.createRulerError)('Invalid configuration file format', `File: ${configFile}, Errors: ${validationResult.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join(', ')}`);
        }
    }
    catch (err) {
        if (err instanceof Error && err.code !== 'ENOENT') {
            if (err.message.includes('[ruler]')) {
                throw err; // Re-throw validation errors
            }
            console.warn(`[ruler] Warning: could not read config file at ${configFile}: ${err.message}`);
        }
        raw = {};
    }
    const defaultAgents = Array.isArray(raw.default_agents)
        ? raw.default_agents.map((a) => String(a))
        : undefined;
    const agentsSection = raw.agents && typeof raw.agents === 'object' && !Array.isArray(raw.agents)
        ? raw.agents
        : {};
    const agentConfigs = {};
    for (const [name, section] of Object.entries(agentsSection)) {
        if (section && typeof section === 'object') {
            const sectionObj = section;
            const cfg = {};
            if (typeof sectionObj.enabled === 'boolean') {
                cfg.enabled = sectionObj.enabled;
            }
            if (typeof sectionObj.output_path === 'string') {
                cfg.outputPath = path.resolve(projectRoot, sectionObj.output_path);
            }
            if (typeof sectionObj.output_path_instructions === 'string') {
                cfg.outputPathInstructions = path.resolve(projectRoot, sectionObj.output_path_instructions);
            }
            if (typeof sectionObj.output_path_config === 'string') {
                cfg.outputPathConfig = path.resolve(projectRoot, sectionObj.output_path_config);
            }
            if (sectionObj.mcp && typeof sectionObj.mcp === 'object') {
                const m = sectionObj.mcp;
                const mcpCfg = {};
                if (typeof m.enabled === 'boolean') {
                    mcpCfg.enabled = m.enabled;
                }
                if (typeof m.merge_strategy === 'string') {
                    const ms = m.merge_strategy;
                    if (ms === 'merge' || ms === 'overwrite') {
                        mcpCfg.strategy = ms;
                    }
                }
                cfg.mcp = mcpCfg;
            }
            agentConfigs[name] = cfg;
        }
    }
    const rawMcpSection = raw.mcp && typeof raw.mcp === 'object' && !Array.isArray(raw.mcp)
        ? raw.mcp
        : {};
    const globalMcpConfig = {};
    if (typeof rawMcpSection.enabled === 'boolean') {
        globalMcpConfig.enabled = rawMcpSection.enabled;
    }
    if (typeof rawMcpSection.merge_strategy === 'string') {
        const strat = rawMcpSection.merge_strategy;
        if (strat === 'merge' || strat === 'overwrite') {
            globalMcpConfig.strategy = strat;
        }
    }
    const rawGitignoreSection = raw.gitignore &&
        typeof raw.gitignore === 'object' &&
        !Array.isArray(raw.gitignore)
        ? raw.gitignore
        : {};
    const gitignoreConfig = {};
    if (typeof rawGitignoreSection.enabled === 'boolean') {
        gitignoreConfig.enabled = rawGitignoreSection.enabled;
    }
    if (typeof rawGitignoreSection.local === 'boolean') {
        gitignoreConfig.local = rawGitignoreSection.local;
    }
    const rawSkillsSection = raw.skills && typeof raw.skills === 'object' && !Array.isArray(raw.skills)
        ? raw.skills
        : {};
    const skillsConfig = {};
    if (typeof rawSkillsSection.enabled === 'boolean') {
        skillsConfig.enabled = rawSkillsSection.enabled;
    }
    const nestedDefined = typeof raw.nested === 'boolean';
    const nested = nestedDefined ? raw.nested : false;
    return {
        defaultAgents,
        agentConfigs,
        cliAgents,
        mcp: globalMcpConfig,
        gitignore: gitignoreConfig,
        skills: skillsConfig,
        nested,
        nestedDefined,
    };
}
