import { readdir, readFile, writeFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import { buildCommandContext, } from '../../../app/command-context.js';
import { readGlobalOptions } from '../../shared/shared.utils.js';
import { fileExists } from '../../../fs/io.js';
import { Command, Option } from 'commander';
import { convertAdmonitions } from './codemod.js';
import { injectFrontmatter } from './frontmatter.js';
function parseMkdocsNavTitles(yamlContent) {
    const titles = new Map();
    const lineRe = /^\s*-\s+(?:'([^']+)'|"([^"]+)")\s*:\s*(.+)\s*$/gm;
    for (const match of yamlContent.matchAll(lineRe)) {
        const title = match[1] ?? match[2];
        const path = match[3].trim().replace(/^['"]|['"]$/g, '');
        titles.set(path, title);
    }
    return titles;
}
async function migrateFiles(cwd, options, deps) {
    const docsDir = join(cwd, options.docsDir);
    const changes = [];
    let navTitles = new Map();
    if (options.config) {
        const configPath = join(cwd, options.config);
        if (await deps.fileExists(configPath)) {
            const yamlContent = await deps.readFile(configPath, 'utf8');
            navTitles = parseMkdocsNavTitles(yamlContent);
        }
    }
    const entries = await deps.readdir(docsDir, {
        withFileTypes: true,
        recursive: true,
    });
    for (const entry of entries) {
        if (!entry.isFile() || !entry.name.endsWith('.md')) {
            continue;
        }
        const parentPath = entry.parentPath ?? entry.path;
        const filePath = join(parentPath, entry.name);
        const relativePath = filePath.slice(docsDir.length + 1);
        const content = await deps.readFile(filePath, 'utf8');
        const admonitionResult = convertAdmonitions(content);
        const mkdocsTitle = navTitles.get(relativePath);
        const frontmatterResult = injectFrontmatter(admonitionResult.content, {
            mkdocsTitle,
            fileName: basename(filePath),
        });
        const hasChanges = admonitionResult.admonitionsConverted > 0 ||
            frontmatterResult.titleInjected ||
            frontmatterResult.descriptionSeeded;
        if (hasChanges) {
            if (options.apply) {
                await deps.writeFile(filePath, frontmatterResult.content, 'utf8');
            }
            changes.push({
                path: relativePath,
                admonitionsConverted: admonitionResult.admonitionsConverted,
                titleInjected: frontmatterResult.titleInjected,
                descriptionSeeded: frontmatterResult.descriptionSeeded,
            });
        }
    }
    return changes;
}
const DEFAULT_FILE_DEPS = {
    readFile,
    writeFile,
    readdir: readdir,
    fileExists,
};
const DEFAULT_DEPENDENCIES = {
    buildCommandContext,
    fileDeps: DEFAULT_FILE_DEPS,
};
async function runMigrateCommand(context, options, dependencies) {
    try {
        const changes = await migrateFiles(context.cwd, options, dependencies.fileDeps);
        const mode = options.apply ? 'applied' : 'dry-run';
        if (context.json) {
            context.logger.json({
                status: 'ok',
                mode,
                filesScanned: changes.length,
                changes,
            });
            process.exitCode = 0;
            return;
        }
        if (changes.length === 0) {
            context.logger.info('No files need migration.');
            process.exitCode = 0;
            return;
        }
        context.logger.info(`Migration ${mode}: ${changes.length} file(s) changed`);
        for (const change of changes) {
            const parts = [];
            if (change.admonitionsConverted > 0) {
                parts.push(`${change.admonitionsConverted} admonition(s)`);
            }
            if (change.titleInjected) {
                parts.push('title injected');
            }
            if (change.descriptionSeeded) {
                parts.push('description seeded');
            }
            context.logger.info(`  ${change.path}: ${parts.join(', ')}`);
        }
        if (!options.apply) {
            context.logger.info('\nRe-run with --apply to write changes.');
        }
        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 = 1;
    }
}
export function createDocsMigrateCommand(overrides = {}) {
    const dependencies = {
        ...DEFAULT_DEPENDENCIES,
        ...overrides,
    };
    return new Command('migrate')
        .description('Migrate MkDocs markdown to Fumadocs format (admonitions, frontmatter)')
        .addOption(new Option('--docs-dir <path>', 'Documentation source directory').default('docs'))
        .addOption(new Option('--config <path>', 'Path to mkdocs.yml for nav title lookup'))
        .option('--apply', 'Apply changes (default: dry-run)', false)
        .action(async (options, command) => {
        const context = dependencies.buildCommandContext(readGlobalOptions(command));
        await runMigrateCommand(context, options, dependencies);
    });
}
