import path from 'node:path';
import { exists, nowIso, writeJsonAtomic } from '../fsx.js';
import { allocateWorkerWorktree } from '../git/git-worktree-manager.js';
import { gitOutputLine, runGitCommand } from '../git/git-worktree-runner.js';
import { loopNodeRoot } from './loop-artifacts.js';
export async function allocateLoopWorktree(input) {
    const blockers = [];
    let worktreeId = null;
    let worktreePath = null;
    let branch = null;
    let baseRef = null;
    if (input.node.worktree.required && !input.noMutation) {
        const gitPresent = await exists(path.join(input.root, '.git'));
        if (!gitPresent) {
            blockers.push('loop_worktree_required_but_git_missing');
        }
        else {
            const allocation = await allocateWorkerWorktree({
                repoRoot: input.root,
                missionId: input.plan.mission_id,
                workerId: input.node.loop_id,
                slotId: input.node.loop_id,
                generationIndex: 1,
                branchPrefix: input.node.worktree.branch_prefix
            }).catch((err) => ({ ok: false, blockers: [`loop_worktree_allocate_exception:${err instanceof Error ? err.message : String(err)}`] }));
            if (allocation.ok) {
                worktreeId = allocation.worker_id || input.node.loop_id;
                worktreePath = allocation.worktree_path || null;
                branch = allocation.branch || null;
                baseRef = allocation.base_ref || null;
            }
            else {
                blockers.push(...stringArray(allocation.blockers));
            }
        }
    }
    const record = {
        schema: 'sks.loop-worktree.v1',
        loop_id: input.node.loop_id,
        worktree_id: worktreeId,
        path: worktreePath,
        branch,
        base_ref: baseRef,
        allocated_at: nowIso(),
        cleanup_policy: input.node.worktree.cleanup,
        blockers
    };
    await writeJsonAtomic(path.join(loopNodeRoot(input.root, input.plan.mission_id, input.node.loop_id), 'worktree.json'), record);
    return record;
}
export async function computeLoopDiff(input) {
    const cwd = input.worktreePath || input.root;
    const blockers = [];
    const names = await runGitCommand(cwd, ['diff', '--name-only', 'HEAD'], { timeoutMs: 30000 }).catch(() => null);
    const stat = await runGitCommand(cwd, ['diff', '--stat', 'HEAD'], { timeoutMs: 30000 }).catch(() => null);
    const diff = await runGitCommand(cwd, ['diff', '--binary', '--full-index', 'HEAD'], { timeoutMs: 60000 }).catch(() => null);
    if (!names?.ok)
        blockers.push('loop_git_diff_name_only_failed');
    if (!diff?.ok)
        blockers.push('loop_git_diff_failed');
    const changedFiles = [...new Set((names?.stdout || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean))];
    blockers.push(...enforceLoopOwnerScope(changedFiles, input.ownerScope));
    return {
        changed_files: changedFiles,
        patch_bytes: Buffer.byteLength(diff?.stdout || ''),
        diff_stat: stat ? gitOutputLine(stat) || stat.stdout.slice(-4000) : '',
        blockers: [...new Set(blockers)]
    };
}
export function enforceLoopOwnerScope(changedFiles, ownerScope) {
    const blockers = [];
    for (const file of changedFiles) {
        if (!isInOwnerScope(file, ownerScope))
            blockers.push(`loop_owner_scope_violation:${file}`);
    }
    return blockers;
}
function isInOwnerScope(file, ownerScope) {
    const normalized = normalizePath(file);
    if (ownerScope.files.map(normalizePath).includes(normalized))
        return true;
    return ownerScope.directories.map(normalizePath).some((dir) => normalized === dir || normalized.startsWith(`${dir}/`));
}
function normalizePath(value) {
    return String(value || '').replace(/\\/g, '/').replace(/^\.\/+/, '');
}
function stringArray(value) {
    if (!Array.isArray(value))
        return [];
    return value.map((item) => String(item || '').trim()).filter(Boolean);
}
//# sourceMappingURL=loop-worktree-runtime.js.map