import { readFile as defaultReadFile, writeFile as defaultWriteFile, } from 'node:fs/promises';
import { basename, join } from 'node:path';
import { buildCommandContext } from '../../../app/command-context.js';
import { getFrontmatterBlock, getFrontmatterField, } from '../../shared/frontmatter.js';
import { removeFrontmatterField, replaceFrontmatter, upsertFrontmatterField, } from '../../shared/frontmatter-write.js';
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
import { readGlobalOptions } from '../../shared/shared.utils.js';
import { generateStateDashboard as defaultGenerateStateDashboard, } from '../../state/generate.js';
import { readOatLocalConfig, setActiveProject, writeOatLocalConfig, } from '../../../config/oat-config.js';
import { dirExists, fileExists } from '../../../fs/io.js';
import { resolveProjectRoot } from '../../../fs/paths.js';
import { assertValidProjectStateFilesystemContent } from '../../../validation/project-state.js';
import { Command } from 'commander';
const DEFAULT_DEPENDENCIES = {
    buildCommandContext,
    resolveProjectRoot,
    resolveProjectsRoot,
    readOatLocalConfig,
    writeOatLocalConfig,
    setActiveProject,
    generateStateDashboard: defaultGenerateStateDashboard,
    readFile: defaultReadFile,
    writeFile: defaultWriteFile,
    dirExists,
    fileExists,
    processEnv: process.env,
    now: () => new Date(),
};
async function maybeResumePausedProject(statePath, projectPath, dependencies) {
    const stateContent = await dependencies.readFile(statePath, 'utf8');
    const frontmatter = getFrontmatterBlock(stateContent);
    if (!frontmatter) {
        throw new Error(`state.md is missing frontmatter: ${statePath}`);
    }
    if (getFrontmatterField(frontmatter, 'oat_lifecycle') !== 'paused') {
        return false;
    }
    let nextBlock = upsertFrontmatterField(frontmatter, 'oat_lifecycle', 'active', true).nextBlock;
    nextBlock = removeFrontmatterField(nextBlock, 'oat_pause_timestamp');
    nextBlock = removeFrontmatterField(nextBlock, 'oat_pause_reason');
    nextBlock = upsertFrontmatterField(nextBlock, 'oat_project_state_updated', dependencies.now().toISOString(), true).nextBlock;
    if (nextBlock !== frontmatter) {
        const nextContent = replaceFrontmatter(stateContent, nextBlock);
        await assertValidProjectStateFilesystemContent(nextContent, {
            filePath: statePath,
            projectPath,
        });
        await dependencies.writeFile(statePath, nextContent, 'utf8');
    }
    return true;
}
async function runProjectOpen(projectName, options, context, dependencies) {
    try {
        const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
        const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
        const projectPath = join(projectsRoot, projectName);
        const fullProjectPath = join(repoRoot, projectPath);
        if (!(await dependencies.dirExists(fullProjectPath))) {
            throw new Error(`Project not found: ${projectName}`);
        }
        const statePath = join(fullProjectPath, 'state.md');
        if (!(await dependencies.fileExists(statePath))) {
            throw new Error(`Project state.md not found: ${statePath}`);
        }
        const localConfig = await dependencies.readOatLocalConfig(repoRoot);
        const previousActiveProject = localConfig.activeProject ?? null;
        if (previousActiveProject === projectPath) {
            if (context.json) {
                context.logger.json({
                    status: 'ok',
                    projectName,
                    projectPath,
                    previousActiveProject,
                    resumedFromPaused: false,
                    reason: options.reason ?? null,
                    message: 'already active',
                });
            }
            else {
                context.logger.info(`Project ${projectName} is already active.`);
            }
            process.exitCode = 0;
            return;
        }
        const resumedFromPaused = await maybeResumePausedProject(statePath, fullProjectPath, dependencies);
        await dependencies.setActiveProject(repoRoot, projectPath);
        if (localConfig.lastPausedProject === projectPath) {
            const updatedConfig = await dependencies.readOatLocalConfig(repoRoot);
            await dependencies.writeOatLocalConfig(repoRoot, {
                ...updatedConfig,
                lastPausedProject: null,
            });
        }
        await dependencies.generateStateDashboard({ repoRoot });
        if (context.json) {
            context.logger.json({
                status: 'ok',
                projectName,
                projectPath,
                previousActiveProject,
                resumedFromPaused,
                reason: options.reason ?? null,
            });
        }
        else {
            if (previousActiveProject) {
                context.logger.info(`Switching from ${basename(previousActiveProject)} to ${projectName}.`);
            }
            else {
                context.logger.info(`Opened project: ${projectName}`);
            }
            if (resumedFromPaused) {
                context.logger.info('Resumed paused project and cleared pause metadata.');
            }
            if (options.reason) {
                context.logger.info(`Reason: ${options.reason}`);
            }
        }
        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 createProjectOpenCommand(overrides = {}) {
    const dependencies = {
        ...DEFAULT_DEPENDENCIES,
        ...overrides,
    };
    return new Command('open')
        .description('Open or switch to an OAT project')
        .argument('<name>', 'Project name')
        .option('--reason <string>', 'Optional reason for opening or switching')
        .action(async (name, options, command) => {
        const context = dependencies.buildCommandContext(readGlobalOptions(command));
        await runProjectOpen(name, options, context, dependencies);
    });
}
