import { randomUUID } from 'crypto';
import { nexusEventBus } from '../../engines/event-bus.js';
import { getActiveLockForWorkItem, getLatestLockForWorkItem, insertConstructionLock, releaseConstructionLockRecord, renewConstructionLockRecord, updateWorkItemStatus, } from '../worklist/crud.js';
const DEFAULT_LEASE_DURATION_MS = 5 * 60 * 1000;
function isExpired(lock, now) {
    return lock.leaseExpiresAt <= now;
}
export async function acquireConstructionLock(operativeId, workItemId, db, providers, options = {}) {
    const now = options.now ?? Date.now();
    const leaseDurationMs = options.leaseDurationMs ?? DEFAULT_LEASE_DURATION_MS;
    const current = getActiveLockForWorkItem(db, workItemId, now);
    if (current?.operativeId === operativeId) {
        return renewConstructionLockRecord(db, current.id, leaseDurationMs, now);
    }
    if (current && current.operativeId !== operativeId) {
        nexusEventBus.emit('architects.constructionlock.contested', {
            workItemId,
            byOperativeId: current.operativeId,
            requestedBy: operativeId,
        });
        return null;
    }
    const latest = getLatestLockForWorkItem(db, workItemId);
    if (latest && latest.releasedAt === null && isExpired(latest, now)) {
        releaseConstructionLockRecord(db, latest.id);
    }
    const hooks = providers.hookRuntime.resolveHookSelectors([], `architects lock ${workItemId}`, 'before-mutate');
    const dispatch = providers.hookRuntime.dispatch('before-mutate', hooks, {
        goal: `Acquire construction lock for ${workItemId}`,
        allowMutateHooks: true,
    });
    const hookRef = dispatch.events.length > 0 ? `hook-dispatch:${workItemId}` : null;
    const lock = insertConstructionLock(db, {
        id: randomUUID(),
        workItemId,
        operativeId,
        hookRef,
        leaseDurationMs,
        leaseRenewedAt: now,
        leaseExpiresAt: now + leaseDurationMs,
    });
    updateWorkItemStatus(db, workItemId, 'claimed');
    db.prepare('UPDATE architects_work_items SET assigned_operative_id=?, construction_lock_id=? WHERE id=?').run(operativeId, lock.id, workItemId);
    nexusEventBus.emit('architects.constructionlock.acquired', {
        lockId: lock.id,
        workItemId,
        operativeId,
    });
    nexusEventBus.emit('architects.workitem.claimed', { workItemId, operativeId });
    return lock;
}
export async function renewConstructionLock(operativeId, workItemId, db, options = {}) {
    const now = options.now ?? Date.now();
    const current = getActiveLockForWorkItem(db, workItemId, now);
    if (!current || current.operativeId !== operativeId)
        return null;
    const leaseDurationMs = options.leaseDurationMs ?? current.leaseDurationMs ?? DEFAULT_LEASE_DURATION_MS;
    const renewed = renewConstructionLockRecord(db, current.id, leaseDurationMs, now);
    if (renewed) {
        nexusEventBus.emit('architects.constructionlock.renewed', {
            lockId: renewed.id,
            workItemId,
            operativeId,
            leaseExpiresAt: renewed.leaseExpiresAt,
        });
    }
    return renewed;
}
export async function releaseConstructionLock(operativeId, workItemId, db, finalStatus) {
    const current = getActiveLockForWorkItem(db, workItemId);
    if (!current || current.operativeId !== operativeId)
        return;
    releaseConstructionLockRecord(db, current.id);
    db.prepare('UPDATE architects_work_items SET status=?, construction_lock_id=NULL WHERE id=?').run(finalStatus, workItemId);
    nexusEventBus.emit('architects.constructionlock.released', {
        lockId: current.id,
        workItemId,
        finalStatus,
    });
}
export function getConstructionLockState(db, workItemId) {
    const lock = getActiveLockForWorkItem(db, workItemId);
    return {
        workItemId,
        locked: Boolean(lock),
        operativeId: lock?.operativeId ?? null,
        leaseExpiresAt: lock?.leaseExpiresAt ?? null,
        leaseRenewedAt: lock?.leaseRenewedAt ?? null,
    };
}
