import { basename, isAbsolute, join } from 'node:path';
import { buildCommandContext } from '../../../app/command-context.js';
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
import { readOatConfig, readOatLocalConfig, } from '../../../config/oat-config.js';
import { CliError } from '../../../errors/cli-error.js';
import { resolveProjectRoot } from '../../../fs/paths.js';
import { archiveProjectOnCompletion, assertDurableArchiveProjectTarget, buildArchiveSnapshotName, buildProjectArchiveS3Uri, resolveArchiveProjectTarget, resolvePrimaryRepoRoot, } from './archive-utils.js';
export function defaultProjectArchivePushCommandDependencies() {
    return {
        buildCommandContext,
        resolveProjectRoot,
        readOatConfig,
        readOatLocalConfig,
        resolveProjectsRoot,
        resolvePrimaryRepoRoot,
        resolveArchiveProjectTarget,
        archiveProjectOnCompletion,
        processEnv: process.env,
        timestamp: () => new Date().toISOString(),
    };
}
function resolveRepoAbsolutePath(repoRoot, targetPath) {
    return isAbsolute(targetPath) ? targetPath : join(repoRoot, targetPath);
}
async function resolveArchiveTarget(dependencies, repoRoot, projectPathArg) {
    const rawProjectPath = projectPathArg?.trim();
    const projectPath = rawProjectPath && rawProjectPath.length > 0
        ? rawProjectPath
        : (await dependencies.readOatLocalConfig(repoRoot)).activeProject?.trim();
    if (!projectPath) {
        throw new CliError('No project path provided and no active project is configured. Pass a project path or set `activeProject`.');
    }
    const absoluteProjectPath = resolveRepoAbsolutePath(repoRoot, projectPath);
    const projectName = basename(absoluteProjectPath);
    if (!projectName) {
        throw new CliError(`Unable to determine project name from \`${projectPath}\`.`);
    }
    return {
        projectName,
        projectPath: absoluteProjectPath,
    };
}
function buildArchiveOptions(repoRoot, config, projectsRoot, target) {
    return {
        repoRoot,
        projectPath: target.projectPath,
        projectName: target.projectName,
        projectsRoot,
        s3Uri: config.archive?.s3Uri,
        s3SyncOnComplete: config.archive?.s3SyncOnComplete ?? false,
        summaryExportPath: config.archive?.summaryExportPath,
        awsProfile: config.archive?.awsProfile,
        awsRegion: config.archive?.awsRegion,
    };
}
async function buildDryRunReport(dependencies, repoRoot, config, target, archiveTarget) {
    const snapshotName = buildArchiveSnapshotName(target.projectName, dependencies.timestamp());
    const remoteRepoRoot = await dependencies.resolvePrimaryRepoRoot(repoRoot);
    const s3Path = config.archive?.s3Uri && config.archive.s3SyncOnComplete === true
        ? buildProjectArchiveS3Uri(config.archive.s3Uri, remoteRepoRoot, snapshotName)
        : null;
    const summaryExportFile = config.archive?.summaryExportPath
        ? join(repoRoot, config.archive.summaryExportPath, `${snapshotName}.md`)
        : null;
    return {
        status: 'ok',
        mode: 'dry-run',
        projectName: target.projectName,
        projectPath: target.projectPath,
        archivePath: archiveTarget.archivePath,
        s3Path,
        summaryExportFile,
        warnings: [],
    };
}
function emitArchivePushText(report, summaryExportPath, logger) {
    for (const warning of report.warnings) {
        logger.warn(warning);
    }
    if (report.mode === 'dry-run') {
        logger.info(`Dry-run: would archive project \`${report.projectName}\` from \`${report.projectPath}\` to \`${report.archivePath}\`.`);
        if (report.s3Path) {
            logger.info(`S3 archive: ${report.s3Path}`);
        }
        if (summaryExportPath) {
            logger.info(`Summary export path: ${summaryExportPath}`);
        }
        return;
    }
    logger.info(`Archived project \`${report.projectName}\` to \`${report.archivePath}\`.`);
    if (report.s3Path) {
        logger.info(`S3 archive: ${report.s3Path}`);
    }
    if (report.summaryExportFile) {
        logger.info(`Summary export: ${report.summaryExportFile}`);
    }
}
function emitArchivePushReport(report, summaryExportPath, context) {
    if (context.json) {
        context.logger.json(report);
        return;
    }
    emitArchivePushText(report, summaryExportPath, context.logger);
}
export async function runArchivePushCommand(dependencies, projectPathArg, options, context) {
    try {
        const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
        const config = await dependencies.readOatConfig(repoRoot);
        const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
        const target = await resolveArchiveTarget(dependencies, repoRoot, projectPathArg);
        const archiveTarget = await dependencies.resolveArchiveProjectTarget({
            repoRoot,
            projectsRoot,
            projectName: target.projectName,
        }, {
            env: dependencies.processEnv,
            timestamp: dependencies.timestamp,
        });
        assertDurableArchiveProjectTarget(archiveTarget);
        const dryRun = options.dryRun === true || context.dryRun;
        if (dryRun) {
            const report = await buildDryRunReport(dependencies, repoRoot, config, target, archiveTarget);
            emitArchivePushReport(report, config.archive?.summaryExportPath, context);
            process.exitCode = 0;
            return;
        }
        const result = await dependencies.archiveProjectOnCompletion(buildArchiveOptions(repoRoot, config, projectsRoot, target));
        const report = {
            status: 'ok',
            mode: 'apply',
            projectName: target.projectName,
            projectPath: target.projectPath,
            archivePath: result.archivePath,
            s3Path: result.s3Path,
            summaryExportFile: result.summaryExportFile,
            warnings: result.warnings,
        };
        emitArchivePushReport(report, config.archive?.summaryExportPath, context);
        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;
    }
}
