import { OAT_VERSION } from '../../shared/oat-version.js';
import { countPlannedOperations } from './sync.utils.js';
function countSkippedEntries(scopePlans) {
    return scopePlans.reduce((total, scopePlan) => {
        const codexSkipped = scopePlan.codexExtension?.operations.filter((operation) => operation.action === 'skip').length ?? 0;
        return (total +
            scopePlan.plan.entries.filter((entry) => entry.operation === 'skip')
                .length +
            codexSkipped);
    }, 0);
}
function buildSummary(scopePlans, applied, failed) {
    return {
        plannedOperations: countPlannedOperations(scopePlans),
        applied,
        failed,
        skipped: countSkippedEntries(scopePlans),
    };
}
function formatAppliedOutput(scopePlans, dependencies) {
    if (scopePlans.length === 0) {
        return dependencies.formatSyncPlan({
            scope: 'project',
            entries: [],
            removals: [],
        }, true);
    }
    return scopePlans
        .map((scopePlan) => {
        const syncOutput = dependencies.formatSyncPlan(scopePlan.plan, true);
        const codexExtension = scopePlan.codexExtension;
        if (!codexExtension) {
            return `Scope: ${scopePlan.scope}\n${syncOutput}`;
        }
        const codexLines = codexExtension.operations.map((operation) => {
            const role = operation.roleName ? ` (${operation.roleName})` : '';
            return `- codex:${operation.target}:${operation.action} ${operation.path}${role} (${operation.reason})`;
        });
        return `Scope: ${scopePlan.scope}\n${syncOutput}\n\nCodex extension (applied)\n${codexLines.join('\n')}`;
    })
        .join('\n\n');
}
export async function runSyncApply(context, scopePlans, dependencies) {
    let applied = 0;
    let failed = 0;
    for (const scopePlan of scopePlans) {
        const hasSyncEntries = scopePlan.plan.entries.length > 0 || scopePlan.plan.removals.length > 0;
        const hasCodexPlannedOperations = scopePlan.codexExtensionPlan?.operations.some((operation) => operation.action !== 'skip') ?? false;
        const shouldRefreshManifestVersion = scopePlan.manifest.oatVersion !== OAT_VERSION;
        if (!hasSyncEntries &&
            !hasCodexPlannedOperations &&
            !shouldRefreshManifestVersion) {
            continue;
        }
        if (hasSyncEntries || shouldRefreshManifestVersion) {
            const result = await dependencies.executeSyncPlan(scopePlan.plan, scopePlan.manifest, scopePlan.manifestPath);
            applied += result.applied;
            failed += result.failed;
        }
        if (scopePlan.codexExtensionPlan && hasCodexPlannedOperations) {
            const codexResult = await dependencies.applyCodexProjectExtensionPlan(scopePlan.scopeRoot, scopePlan.codexExtensionPlan);
            applied += codexResult.applied;
            failed += codexResult.failed;
            if (scopePlan.codexExtension) {
                scopePlan.codexExtension = {
                    ...scopePlan.codexExtension,
                    applied: codexResult.applied,
                    failed: codexResult.failed,
                    skipped: codexResult.skipped,
                };
            }
        }
    }
    const summary = buildSummary(scopePlans, applied, failed);
    const providerMismatches = scopePlans
        .map((scopePlan) => scopePlan.providerMismatches)
        .filter((mismatch) => mismatch !== undefined);
    const codexExtensions = scopePlans
        .map((scopePlan) => scopePlan.codexExtension)
        .filter((extension) => extension !== undefined);
    if (context.json) {
        context.logger.json({
            scope: context.scope,
            dryRun: false,
            plans: scopePlans.map((scopePlan) => scopePlan.plan),
            summary,
            providerMismatches,
            codexExtensions,
        });
    }
    else {
        context.logger.info(formatAppliedOutput(scopePlans, dependencies));
        if (summary.plannedOperations === 0) {
            context.logger.info('\nNo changes required.');
        }
        else if (failed > 0) {
            context.logger.warn('\nSync completed with partial failures.');
        }
        else {
            context.logger.success('\nSync applied successfully.');
        }
    }
    process.exitCode = failed > 0 ? 1 : 0;
}
