import path from 'node:path';
import { nowIso, writeJsonAtomic } from '../fsx.js';
import { applyAgentPatchQueueEntry, rollbackAgentPatchApply } from './agent-patch-apply-worker.js';
import { AgentPatchTransactionJournal } from './agent-patch-transaction-journal.js';
export const AGENT_PATCH_CONFLICT_REBASE_SCHEMA = 'sks.agent-patch-conflict-rebase.v1';
export const AGENT_PATCH_CONFLICT_REBASE_ARTIFACT = 'agent-patch-conflict-rebase-results.json';
export async function executeAgentPatchConflictRebase(root, entries, merge, opts = {}) {
    const byId = new Map(entries.map((entry) => [entry.id, entry]));
    const groups = Array.isArray(merge?.serial_merge_groups) ? merge.serial_merge_groups : [];
    const groupResults = [];
    const applyResults = [];
    for (const group of groups) {
        const reason = String(group?.reason || '');
        const policy = serialRetryAllowed(reason, opts) ? 'serial_retry' : 'blocked';
        const attempts = [];
        const blockers = [];
        const groupEntries = (Array.isArray(group?.entry_ids) ? group.entry_ids : []).map((id) => byId.get(String(id))).filter(Boolean);
        if (policy === 'blocked') {
            blockers.push(`serial_rebase_blocked_by_policy:${reason || 'unknown'}`);
            for (const entry of groupEntries) {
                attempts.push({ entry_id: entry.id, agent_id: entry.agent_id, attempt: 0, status: 'blocked', changed_files: [], violations: blockers });
            }
        }
        else {
            let attempt = 0;
            for (const entry of groupEntries) {
                attempt += 1;
                const applyResult = await safelyApplySerialRebaseEntry(root, entry, {
                    dryRun: opts.dryRun === true,
                    ...(opts.artifactsDir ? { artifactsDir: opts.artifactsDir } : {})
                });
                const rollbackDryRun = applyResult?.ok === true
                    ? await runSerialRebaseRollbackDryRun(root, applyResult, opts.artifactsDir)
                    : null;
                const violations = [
                    ...(Array.isArray(applyResult?.violations) ? applyResult.violations.map(String) : []),
                    ...(rollbackDryRun && rollbackDryRun.ok === false ? (rollbackDryRun.violations || ['rollback_dry_run_failed']).map(String) : [])
                ];
                const attemptOk = applyResult?.ok === true && rollbackDryRun?.ok !== false;
                applyResults.push({ ...applyResult, rollback_dry_run: rollbackDryRun, serial_rebase_group_id: String(group?.group_id || ''), rebase_attempt: attempt });
                attempts.push({
                    entry_id: entry.id,
                    agent_id: entry.agent_id,
                    attempt,
                    status: attemptOk ? 'rebased' : 'blocked',
                    changed_files: Array.isArray(applyResult?.changed_files) ? applyResult.changed_files.map(String) : [],
                    violations
                });
                if (!attemptOk)
                    blockers.push(...(violations.length ? violations : [`serial_rebase_failed:${entry.id}`]));
            }
        }
        groupResults.push({
            group_id: String(group?.group_id || `serial-${groupResults.length + 1}`),
            reason,
            file: group?.file ? String(group.file) : null,
            policy,
            attempts,
            blockers: [...new Set(blockers)]
        });
    }
    const succeeded = groupResults.flatMap((group) => group.attempts.filter((attempt) => attempt.status === 'rebased').map((attempt) => attempt.entry_id));
    const failed = groupResults.flatMap((group) => group.attempts.filter((attempt) => attempt.status === 'blocked' && group.policy === 'serial_retry').map((attempt) => attempt.entry_id));
    const blocked = groupResults.flatMap((group) => group.attempts.filter((attempt) => attempt.status === 'blocked' && group.policy === 'blocked').map((attempt) => attempt.entry_id));
    const blockers = [...new Set(groupResults.flatMap((group) => group.blockers))];
    const result = {
        schema: AGENT_PATCH_CONFLICT_REBASE_SCHEMA,
        generated_at: nowIso(),
        ok: failed.length === 0 && blocked.length === 0,
        dry_run: opts.dryRun === true,
        group_results: groupResults,
        apply_results: applyResults,
        succeeded_entry_ids: [...new Set(succeeded)],
        failed_entry_ids: [...new Set(failed)],
        blocked_entry_ids: [...new Set(blocked)],
        rebase_attempt_count: groupResults.reduce((sum, group) => sum + group.attempts.filter((attempt) => attempt.attempt > 0).length, 0),
        blockers
    };
    if (opts.artifactsDir)
        await writeJsonAtomic(path.join(opts.artifactsDir, AGENT_PATCH_CONFLICT_REBASE_ARTIFACT), result);
    return result;
}
async function safelyApplySerialRebaseEntry(root, entry, opts) {
    try {
        return await applyAgentPatchQueueEntry(root, entry, opts);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            schema: 'sks.agent-patch-apply-result.v1',
            entry_id: entry.id,
            agent_id: entry.agent_id,
            lease_id: entry.lease_id || entry.envelope?.lease_id || entry.envelope?.lease_proof?.lease_id || null,
            ok: false,
            status: 'blocked',
            dry_run: opts.dryRun === true,
            changed_files: [],
            rollback: [],
            rollback_digest: null,
            before_hashes: {},
            after_hashes: {},
            verification: { status: 'blocked', checks: ['serial-rebase-exception'] },
            violations: [`serial_rebase_exception:${message}`]
        };
    }
}
function serialRetryAllowed(reason, opts) {
    if (/protected_path|lease_path_not_allowed|entry_not_pending/.test(reason))
        return false;
    if (/domain_conflict/.test(reason))
        return opts.allowDomainRetry === true;
    return /parallel_write_conflict|subtree_write_conflict|stale_context/.test(reason);
}
async function runSerialRebaseRollbackDryRun(root, applyResult, artifactsDir) {
    const journal = artifactsDir ? new AgentPatchTransactionJournal(artifactsDir) : null;
    await journal?.append({
        event_type: 'rollback_dry_run_started',
        entry_id: applyResult.entry_id || 'unknown-entry',
        agent_id: applyResult.agent_id || null,
        lease_id: applyResult.lease_id || null,
        status: 'started',
        changed_files: applyResult.changed_files || [],
        rollback_digest: applyResult.rollback_digest || null
    });
    const rollbackDryRun = await rollbackAgentPatchApply(root, applyResult, { dryRun: true });
    await journal?.append({
        event_type: 'rollback_dry_run_finished',
        entry_id: applyResult.entry_id || 'unknown-entry',
        agent_id: applyResult.agent_id || null,
        lease_id: applyResult.lease_id || null,
        status: rollbackDryRun.status || null,
        changed_files: applyResult.changed_files || [],
        rollback_digest: rollbackDryRun.rollback_digest || applyResult.rollback_digest || null,
        violations: rollbackDryRun.violations || []
    });
    return rollbackDryRun;
}
//# sourceMappingURL=agent-patch-conflict-rebase.js.map