import { readFile, writeFile } from 'node:fs/promises';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import { parseCanonicalAgentFile } from '../../../agents/canonical/index.js';
import { buildCommandContext } from '../../../app/command-context.js';
import { readGlobalOptions } from '../../shared/shared.utils.js';
import { CliError } from '../../../errors/index.js';
import { ensureDir, fileExists } from '../../../fs/io.js';
import { resolveProjectRoot, resolveScopeRoot } from '../../../fs/paths.js';
import { mergeCodexConfigForRole, readCodexMaxDepth, } from '../../../providers/codex/codec/config-merge.js';
import { materializeCodexRole } from '../../../providers/codex/codec/materialize.js';
import { withOatManagedCodexRoleOwner } from '../../../providers/codex/codec/shared.js';
import { Command, Option } from 'commander';
function createDependencies() {
    return {
        buildCommandContext,
        async resolveScopeRoot(scope, context) {
            if (scope === 'project') {
                return resolveProjectRoot(context.cwd);
            }
            return resolveScopeRoot(scope, context.cwd, context.home);
        },
        parseCanonicalAgentFile,
        materializeCodexRole,
    };
}
function requireOption(value, optionName) {
    const normalized = value?.trim() ?? '';
    if (!normalized) {
        throw new CliError(`Missing required ${optionName}. Pass ${optionName} <value>.`);
    }
    return normalized;
}
function resolveAgentPath(agentName, options, scopeRoot) {
    if (options.agentPath) {
        return isAbsolute(options.agentPath)
            ? options.agentPath
            : resolve(scopeRoot, options.agentPath);
    }
    const fileName = agentName.endsWith('.md') ? agentName : `${agentName}.md`;
    return join(scopeRoot, '.agents', 'agents', fileName);
}
export function formatCodexMaterializeResult(result) {
    return [
        result.dryRun
            ? 'Codex materialization preview'
            : 'Codex materialization complete',
        `Scope: ${result.scope}`,
        `Agent: ${result.agentPath}`,
        `Role: ${result.roleName}`,
        `Role file: ${result.rolePath}`,
        `Config file: ${result.configPath}`,
        'TOML preview:',
        result.tomlPreview.trimEnd(),
    ].join('\n');
}
async function buildCodexMaterializePlan(agentName, options, context, dependencies) {
    const model = requireOption(options.model, '--model');
    const effort = requireOption(options.effort, '--effort');
    const scope = options.scope;
    const scopeRoot = await dependencies.resolveScopeRoot(scope, context);
    const agentPath = resolveAgentPath(agentName, options, scopeRoot);
    const agent = await dependencies.parseCanonicalAgentFile(agentPath);
    const materializedRole = dependencies.materializeCodexRole({
        agent,
        model,
        effort,
        roleName: options.roleName,
    });
    const role = {
        ...materializedRole,
        content: withOatManagedCodexRoleOwner(materializedRole.content, scope === 'user' ? 'user-config' : 'project-config'),
    };
    const rolePath = join(scopeRoot, '.codex', role.configFile);
    const configPath = join(scopeRoot, '.codex', 'config.toml');
    const existingRoleContent = await readOptionalFile(rolePath);
    const existingConfigContent = await readOptionalFile(configPath);
    let inheritedMaxDepth;
    if (scope === 'project') {
        const userScopeRoot = await dependencies.resolveScopeRoot('user', context);
        const userConfigContent = await readOptionalFile(join(userScopeRoot, '.codex', 'config.toml'));
        inheritedMaxDepth = readCodexMaxDepth(userConfigContent) ?? undefined;
    }
    const mergedConfig = mergeCodexConfigForRole({
        existingContent: existingConfigContent,
        role: {
            roleName: role.roleName,
            description: role.description,
            configFile: role.configFile,
        },
        inheritedMaxDepth,
    });
    return {
        configChanged: mergedConfig.changed,
        mergedConfigContent: mergedConfig.mergedContent,
        role,
        roleChanged: existingRoleContent === null ||
            existingRoleContent.trimEnd() !== role.content.trimEnd(),
        result: {
            status: context.dryRun ? 'preview' : 'written',
            dryRun: context.dryRun,
            scope,
            agentPath,
            roleName: role.roleName,
            rolePath,
            configPath,
            configFile: role.configFile,
            tomlPreview: role.content,
        },
    };
}
async function readOptionalFile(filePath) {
    if (!(await fileExists(filePath))) {
        return null;
    }
    return readFile(filePath, 'utf8');
}
async function applyCodexMaterializePlan({ configChanged, mergedConfigContent, result, role, roleChanged, }) {
    if (roleChanged) {
        await ensureDir(dirname(result.rolePath));
        await writeFile(result.rolePath, role.content, 'utf8');
    }
    if (configChanged) {
        await ensureDir(dirname(result.configPath));
        await writeFile(result.configPath, mergedConfigContent, 'utf8');
    }
}
async function runCodexMaterializeCommand(agentName, options, context, dependencies) {
    try {
        const plan = await buildCodexMaterializePlan(agentName, options, context, dependencies);
        if (!context.dryRun) {
            await applyCodexMaterializePlan(plan);
        }
        const { result } = plan;
        if (context.json) {
            context.logger.json(result);
        }
        else {
            context.logger.info(formatCodexMaterializeResult(result));
        }
        process.exitCode = 0;
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        if (context.json) {
            context.logger.json({ status: 'error', message });
        }
        else {
            context.logger.error(message);
        }
        process.exitCode = error instanceof CliError ? error.exitCode : 1;
    }
}
export function createCodexMaterializeCommand(overrides = {}) {
    const dependencies = {
        ...createDependencies(),
        ...overrides,
    };
    return new Command('materialize')
        .description('Materialize a canonical agent as a Codex role')
        .argument('<agent-name>', 'Canonical agent name')
        .addOption(new Option('--scope <scope>', 'Materialization scope')
        .choices(['project', 'user'])
        .default('project'))
        .option('--model <model>', 'Codex model ID')
        .option('--effort <effort>', 'Codex reasoning effort')
        .option('--role-name <role>', 'Override generated Codex role name')
        .option('--agent-path <path>', 'Path to canonical agent markdown')
        .option('--dry-run', 'Preview files without writing')
        .action(async (agentName, options, command) => {
        const context = dependencies.buildCommandContext(readGlobalOptions(command));
        await runCodexMaterializeCommand(agentName, options, context, dependencies);
    });
}
