import { randomUUID } from 'crypto';
function parseArray(value) {
    if (!value)
        return [];
    try {
        return JSON.parse(value);
    }
    catch {
        return [];
    }
}
function parseNumber(value, fallback) {
    const next = Number(value);
    return Number.isFinite(next) ? next : fallback;
}
function getWorkItemAncestry(db, workItemId) {
    if (!workItemId)
        return [];
    const row = db.prepare('SELECT ancestry_path FROM architects_work_items WHERE id=?').get(workItemId);
    const ancestry = parseArray(row?.ancestry_path);
    return [...ancestry, workItemId];
}
function parseObject(value) {
    if (!value)
        return {};
    try {
        return JSON.parse(value);
    }
    catch {
        return {};
    }
}
export function mapBlueprint(row) {
    return {
        id: row.id,
        strikeTeamId: row.strike_team_id ?? null,
        title: row.title,
        workflowId: row.workflow_id ?? null,
        status: row.status,
        variables: parseObject(row.variables),
        worklistId: row.worklist_id ?? null,
        createdAt: row.created_at,
    };
}
export function mapWorklist(row) {
    return {
        id: row.id,
        blueprintId: row.blueprint_id,
        title: row.title,
        createdAt: row.created_at,
    };
}
export function mapWorkItem(row) {
    return {
        id: row.id,
        worklistId: row.worklist_id,
        title: row.title,
        parentWorkItemId: row.parent_work_item_id ?? null,
        correlationId: row.correlation_id ?? null,
        ancestryPath: parseArray(row.ancestry_path),
        status: row.status,
        dependsOn: parseArray(row.depends_on),
        assignedOperativeId: row.assigned_operative_id ?? null,
        constructionLockId: row.construction_lock_id ?? null,
        branch: row.branch ?? null,
        mergedAt: row.merged_at ?? null,
        createdAt: row.created_at,
    };
}
export function mapConstructionLock(row) {
    return {
        id: row.id,
        workItemId: row.work_item_id,
        operativeId: row.operative_id,
        acquiredAt: row.acquired_at,
        releasedAt: row.released_at ?? null,
        leaseDurationMs: parseNumber(row.lease_duration_ms, 300_000),
        leaseRenewedAt: row.lease_renewed_at == null ? null : parseNumber(row.lease_renewed_at, 0),
        leaseExpiresAt: parseNumber(row.lease_expires_at, 0),
        hookRef: row.hook_ref ?? null,
    };
}
export function mapRelayMessage(row) {
    return {
        id: row.id,
        fromOperativeId: row.from_operative_id,
        toOperativeId: row.to_operative_id ?? null,
        strikeTeamId: row.strike_team_id ?? null,
        subject: row.subject,
        body: row.body,
        sentAt: row.sent_at,
        readAt: row.read_at ?? null,
        priority: row.priority,
    };
}
export function mapConvergenceRun(row) {
    return {
        id: row.id,
        worklistId: row.worklist_id,
        strategy: row.strategy,
        workItemIds: parseArray(row.work_item_ids),
        status: row.status,
        failedItemId: row.failed_item_id ?? null,
        startedAt: row.started_at,
        completedAt: row.completed_at ?? null,
    };
}
export function insertBlueprint(db, input) {
    const id = input.id ?? randomUUID();
    db.prepare(`
    INSERT INTO architects_blueprints (id, strike_team_id, title, workflow_id, status, variables, worklist_id, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, COALESCE(?, datetime('now')))
  `).run(id, input.strikeTeamId ?? null, input.title, input.workflowId ?? null, input.status ?? 'active', JSON.stringify(input.variables ?? {}), input.worklistId ?? null, input.createdAt ?? null);
    return getBlueprint(db, id);
}
export function getBlueprint(db, blueprintId) {
    const row = db.prepare('SELECT * FROM architects_blueprints WHERE id=?').get(blueprintId);
    return row ? mapBlueprint(row) : null;
}
export function insertWorklist(db, input) {
    const id = input.id ?? randomUUID();
    db.prepare('INSERT INTO architects_worklists (id, blueprint_id, title, created_at) VALUES (?, ?, ?, COALESCE(?, datetime(\'now\')))').run(id, input.blueprintId, input.title, input.createdAt ?? null);
    return getWorklist(db, id);
}
export function getWorklist(db, worklistId) {
    const row = db.prepare('SELECT * FROM architects_worklists WHERE id=?').get(worklistId);
    return row ? mapWorklist(row) : null;
}
export function insertWorkItem(db, input) {
    const id = input.id ?? randomUUID();
    const parent = input.parentWorkItemId ? getWorkItem(db, input.parentWorkItemId) : null;
    const ancestryPath = input.ancestryPath ?? getWorkItemAncestry(db, input.parentWorkItemId);
    const correlationId = input.correlationId ?? parent?.correlationId ?? input.worklistId;
    db.prepare(`
      INSERT INTO architects_work_items (
      id, worklist_id, title, parent_work_item_id, correlation_id, ancestry_path, status, depends_on, assigned_operative_id,
      construction_lock_id, branch, merged_at, created_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, datetime('now')))
  `).run(id, input.worklistId, input.title, input.parentWorkItemId ?? null, correlationId, JSON.stringify(ancestryPath), input.status ?? 'todo', JSON.stringify(input.dependsOn ?? []), input.assignedOperativeId ?? null, input.constructionLockId ?? null, input.branch ?? null, input.mergedAt ?? null, input.createdAt ?? null);
    return getWorkItem(db, id);
}
export function getWorkItem(db, workItemId) {
    const row = db.prepare('SELECT * FROM architects_work_items WHERE id=?').get(workItemId);
    return row ? mapWorkItem(row) : null;
}
export function getWorkItemsForWorklist(db, worklistId) {
    return db.prepare('SELECT * FROM architects_work_items WHERE worklist_id=? ORDER BY created_at ASC').all(worklistId).map(mapWorkItem);
}
export function upsertWorkItem(db, input) {
    const existing = getWorkItem(db, input.id);
    if (existing) {
        const parent = input.parentWorkItemId ? getWorkItem(db, input.parentWorkItemId) : existing.parentWorkItemId ? getWorkItem(db, existing.parentWorkItemId) : null;
        const ancestryPath = input.ancestryPath ?? existing.ancestryPath ?? getWorkItemAncestry(db, input.parentWorkItemId ?? existing.parentWorkItemId);
        const correlationId = input.correlationId ?? existing.correlationId ?? parent?.correlationId ?? input.worklistId;
        db.prepare(`
      UPDATE architects_work_items
      SET worklist_id=?, title=?, parent_work_item_id=?, correlation_id=?, ancestry_path=?, status=?, depends_on=?, assigned_operative_id=?, construction_lock_id=?, branch=?, merged_at=?
      WHERE id=?
    `).run(input.worklistId, input.title, input.parentWorkItemId ?? existing.parentWorkItemId, correlationId, JSON.stringify(ancestryPath), input.status ?? existing.status, JSON.stringify(input.dependsOn ?? existing.dependsOn), input.assignedOperativeId ?? existing.assignedOperativeId, input.constructionLockId ?? existing.constructionLockId, input.branch ?? existing.branch, input.mergedAt ?? existing.mergedAt, input.id);
        return getWorkItem(db, input.id);
    }
    return insertWorkItem(db, input);
}
export function updateWorkItemStatus(db, workItemId, status, branch) {
    db.prepare(`
    UPDATE architects_work_items
    SET status=?, branch=COALESCE(?, branch), merged_at=CASE WHEN ?='done' THEN COALESCE(merged_at, datetime('now')) ELSE merged_at END
    WHERE id=?
  `).run(status, branch ?? null, status, workItemId);
    return getWorkItem(db, workItemId);
}
export function insertConstructionLock(db, input) {
    const id = input.id ?? randomUUID();
    const leaseDurationMs = input.leaseDurationMs ?? 300_000;
    const leaseExpiresAt = input.leaseExpiresAt ?? Date.now() + leaseDurationMs;
    db.prepare(`
    INSERT INTO architects_construction_locks (id, work_item_id, operative_id, acquired_at, released_at, hook_ref)
    VALUES (?, ?, ?, COALESCE(?, datetime('now')), ?, ?)
  `).run(id, input.workItemId, input.operativeId, input.acquiredAt ?? null, input.releasedAt ?? null, input.hookRef ?? null);
    db.prepare(`
    UPDATE architects_construction_locks
    SET lease_duration_ms=?, lease_renewed_at=?, lease_expires_at=?
    WHERE id=?
  `).run(leaseDurationMs, input.leaseRenewedAt ?? null, leaseExpiresAt, id);
    return getConstructionLock(db, id);
}
export function getConstructionLock(db, lockId) {
    const row = db.prepare('SELECT * FROM architects_construction_locks WHERE id=?').get(lockId);
    return row ? mapConstructionLock(row) : null;
}
export function getLatestLockForWorkItem(db, workItemId) {
    const row = db.prepare('SELECT * FROM architects_construction_locks WHERE work_item_id=? ORDER BY acquired_at DESC LIMIT 1').get(workItemId);
    return row ? mapConstructionLock(row) : null;
}
export function getActiveLockForWorkItem(db, workItemId, now = Date.now()) {
    const row = db.prepare('SELECT * FROM architects_construction_locks WHERE work_item_id=? AND released_at IS NULL ORDER BY acquired_at DESC LIMIT 1').get(workItemId);
    if (!row)
        return null;
    const lock = mapConstructionLock(row);
    return lock.leaseExpiresAt > now ? lock : null;
}
export function renewConstructionLockRecord(db, lockId, leaseDurationMs = 300_000, now = Date.now()) {
    db.prepare(`
    UPDATE architects_construction_locks
    SET lease_duration_ms=?, lease_renewed_at=?, lease_expires_at=?, released_at=NULL
    WHERE id=? AND released_at IS NULL
  `).run(leaseDurationMs, now, now + leaseDurationMs, lockId);
    return getConstructionLock(db, lockId);
}
export function releaseConstructionLockRecord(db, lockId) {
    db.prepare('UPDATE architects_construction_locks SET released_at=datetime(\'now\') WHERE id=?').run(lockId);
    return getConstructionLock(db, lockId);
}
export function insertRelayMessage(db, input) {
    const id = input.id ?? randomUUID();
    db.prepare(`
    INSERT INTO architects_relay_messages (
      id, from_operative_id, to_operative_id, strike_team_id, subject, body, sent_at, read_at, priority
    ) VALUES (?, ?, ?, ?, ?, ?, COALESCE(?, datetime('now')), ?, ?)
  `).run(id, input.fromOperativeId, input.toOperativeId ?? null, input.strikeTeamId ?? null, input.subject, input.body, input.sentAt ?? null, input.readAt ?? null, input.priority ?? 'normal');
    return getRelayMessage(db, id);
}
export function getRelayMessage(db, messageId) {
    const row = db.prepare('SELECT * FROM architects_relay_messages WHERE id=?').get(messageId);
    return row ? mapRelayMessage(row) : null;
}
export function getRelayInbox(db, input) {
    const rows = db.prepare(`
    SELECT *
    FROM architects_relay_messages
    WHERE (
      (? IS NOT NULL AND to_operative_id = ?)
      OR (? IS NOT NULL AND strike_team_id = ?)
    )
    ORDER BY sent_at DESC
  `).all(input.operativeId ?? null, input.operativeId ?? null, input.strikeTeamId ?? null, input.strikeTeamId ?? null);
    const messages = rows.map(mapRelayMessage);
    if (input.markRead) {
        messages.forEach((message) => {
            db.prepare('UPDATE architects_relay_messages SET read_at=datetime(\'now\') WHERE id=?').run(message.id);
        });
    }
    return messages;
}
export function insertConvergenceRun(db, input) {
    const id = input.id ?? randomUUID();
    db.prepare(`
    INSERT INTO architects_convergence_runs (
      id, worklist_id, strategy, work_item_ids, status, failed_item_id, started_at, completed_at
    ) VALUES (?, ?, ?, ?, ?, ?, COALESCE(?, datetime('now')), ?)
  `).run(id, input.worklistId, input.strategy ?? 'bisecting', JSON.stringify(input.workItemIds ?? []), input.status ?? 'running', input.failedItemId ?? null, input.startedAt ?? null, input.completedAt ?? null);
    return getConvergenceRun(db, id);
}
export function getConvergenceRun(db, runId) {
    const row = db.prepare('SELECT * FROM architects_convergence_runs WHERE id=?').get(runId);
    return row ? mapConvergenceRun(row) : null;
}
export function finalizeConvergenceRun(db, runId, status, failedItemId) {
    db.prepare('UPDATE architects_convergence_runs SET status=?, failed_item_id=?, completed_at=datetime(\'now\') WHERE id=?').run(status, failedItemId ?? null, runId);
    return getConvergenceRun(db, runId);
}
