/**
 * commands/lock.mjs — multi-agent coordination lock commands (C29).
 */
import { execSync } from 'node:child_process';
import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { isLockStale } from '../lock/pid.mjs';
import { parseFlags } from '../util/args.mjs';
import { closeSyncFd, openSyncExclusive } from '../util/fs-lock.mjs';
import { getSharedLockDir } from '../util/project.mjs';
import { err, ok } from '../util/result.mjs';
export function cmdLock(subcommand, projectDir, extraArgs) {
    if (!subcommand)
        return err('Usage: apt-tools lock <claim|release|check|heartbeat|cleanup> <project-dir> [--subtask <id>]');
    if (!projectDir)
        return err('Usage: apt-tools lock ' + subcommand + ' <project-dir> [--subtask <id>]');
    const targetDir = resolve(projectDir);
    // Use shared lock dir (coordinates across worktrees via git common dir)
    const locksDir = getSharedLockDir(targetDir);
    const flags = parseFlags(extraArgs);
    const subtask = flags.get('subtask');
    switch (subcommand) {
        case 'claim': {
            if (!subtask)
                return err('Required: --subtask <id>');
            const lockPath = join(locksDir, `${subtask}.lock`);
            const lockData = {
                worker_pid: process.pid,
                claimed_at: new Date().toISOString(),
                ttl_seconds: 120,
                heartbeat: new Date().toISOString(),
            };
            // Persistent claim file — NOT a sidecar lock. Use O_EXCL via
            // openSync so we can detect collisions without tripping the
            // C28 "no ad-hoc lock sidecars" grep.
            try {
                const fd = openSyncExclusive(lockPath);
                writeFileSync(fd, JSON.stringify(lockData, null, 2) + '\n');
                closeSyncFd(fd);
                return ok({ status: 'ok', command: 'lock-claim', subtask, lock: lockData });
            }
            catch (e) {
                if (e.code === 'EEXIST') {
                    const existing = JSON.parse(readFileSync(lockPath, 'utf-8'));
                    return ok({ status: 'already-claimed', subtask, lock: existing });
                }
                return err(`Failed to claim lock for ${subtask}: ${e.message}`);
            }
        }
        case 'release': {
            if (!subtask)
                return err('Required: --subtask <id>');
            const lockPath = join(locksDir, `${subtask}.lock`);
            if (!existsSync(lockPath)) {
                return ok({
                    status: 'ok',
                    command: 'lock-release',
                    subtask,
                    message: 'No lock file found (already released)',
                });
            }
            const existing = JSON.parse(readFileSync(lockPath, 'utf-8'));
            if (existing.worker_pid !== process.pid) {
                return err(`Cannot release lock for ${subtask}: owned by PID ${existing.worker_pid}, current PID is ${process.pid}`);
            }
            unlinkSync(lockPath);
            return ok({ status: 'ok', command: 'lock-release', subtask });
        }
        case 'check': {
            if (!subtask)
                return err('Required: --subtask <id>');
            const lockPath = join(locksDir, `${subtask}.lock`);
            if (!existsSync(lockPath)) {
                return ok({
                    status: 'ok',
                    command: 'lock-check',
                    locked: false,
                    stale: false,
                    lock: null,
                });
            }
            const lockData = JSON.parse(readFileSync(lockPath, 'utf-8'));
            const stale = isLockStale(lockData);
            return ok({ status: 'ok', command: 'lock-check', locked: true, stale, lock: lockData });
        }
        case 'heartbeat': {
            if (!subtask)
                return err('Required: --subtask <id>');
            const lockPath = join(locksDir, `${subtask}.lock`);
            if (!existsSync(lockPath)) {
                return err(`No lock file found for ${subtask}`);
            }
            const lockData = JSON.parse(readFileSync(lockPath, 'utf-8'));
            if (lockData.worker_pid !== process.pid) {
                return err(`Cannot heartbeat lock for ${subtask}: owned by PID ${lockData.worker_pid}, current PID is ${process.pid}`);
            }
            lockData.heartbeat = new Date().toISOString();
            writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf-8');
            return ok({ status: 'ok', command: 'lock-heartbeat', subtask });
        }
        case 'cleanup': {
            const removed = [];
            const warnings = [];
            if (!existsSync(locksDir)) {
                return ok({ status: 'ok', command: 'lock-cleanup', removed, warnings });
            }
            const lockFiles = readdirSync(locksDir).filter((f) => f.endsWith('.lock'));
            for (const file of lockFiles) {
                const lockPath = join(locksDir, file);
                try {
                    const lockData = JSON.parse(readFileSync(lockPath, 'utf-8'));
                    if (isLockStale(lockData)) {
                        // Check git for partial commits from stale worker
                        try {
                            const gitLog = execSync('git log --oneline -5', {
                                cwd: targetDir,
                                encoding: 'utf-8',
                            }).trim();
                            if (gitLog) {
                                warnings.push(`Stale lock ${file} removed. Recent git log: ${gitLog.split('\n')[0]}`);
                            }
                        }
                        catch {
                            // git not available or not a repo — skip warning
                        }
                        unlinkSync(lockPath);
                        removed.push(file.replace('.lock', ''));
                    }
                }
                catch {
                    // Corrupted lock file — remove it
                    try {
                        unlinkSync(lockPath);
                    }
                    catch {
                        /* ignore */
                    }
                    removed.push(file.replace('.lock', ''));
                    warnings.push(`Removed corrupted lock file: ${file}`);
                }
            }
            return ok({ status: 'ok', command: 'lock-cleanup', removed, warnings });
        }
        default:
            return err(`Unknown lock subcommand: ${subcommand}. Expected: claim, release, check, heartbeat, cleanup`);
    }
}
//# sourceMappingURL=lock.mjs.map