import path from 'path';
import { PersistentWorkLedger } from '../engines/work-ledger.js';
import { createHookRuntime } from '../engines/hook-runtime.js';
import { createWorkflowRuntime } from '../engines/workflow-runtime.js';
import { nexusNetRelay } from '../engines/nexusnet-relay.js';
import { nexusEventBus } from '../engines/event-bus.js';
import { createEngineHealthStatus } from '../engines/runtime-health.js';
import { ArchitectsConfig } from './config.js';
import { openArchitectsDb, resolveArchitectsPaths } from './db/client.js';
import { instantiateBlueprint } from './blueprints/instantiate.js';
import { acquireConstructionLock, releaseConstructionLock, renewConstructionLock } from './construction-locks/manager.js';
import { sendRelay } from './relay/messenger.js';
import { getRelayInbox } from './relay/inbox.js';
import { ArchitectsWard } from './ward/supervisor.js';
import { ArchitectsSentinel } from './sentinel/patrol.js';
import { runConvergenceQueue, setConvergencePaused } from './convergence/queue.js';
import { DispatchGovernor } from './dispatch/governor.js';
import { getWorklist, getWorkItemsForWorklist, insertBlueprint, insertWorklist, upsertWorkItem, updateWorkItemStatus } from './worklist/crud.js';
function parseTimestamp(value) {
    if (typeof value === 'number' && Number.isFinite(value))
        return value;
    if (typeof value === 'string') {
        const normalized = value.includes('T') ? value : `${value.replace(' ', 'T')}Z`;
        const parsed = Date.parse(normalized);
        if (Number.isFinite(parsed))
            return parsed;
    }
    return 0;
}
export function initArchitects(options) {
    if (!ArchitectsConfig.enabled && !options.forceEnable)
        return null;
    let opened;
    const requestedStorageRoot = options.requestedStorageRoot ?? options.repoRoot;
    const initialStorage = resolveArchitectsPaths({
        requestedRoot: requestedStorageRoot,
        storageRoot: options.storageRoot,
    });
    try {
        opened = openArchitectsDb({
            requestedRoot: requestedStorageRoot,
            storageRoot: options.storageRoot,
        });
    }
    catch (err) {
        console.error(`[Architects] Failed to open DB at ${initialStorage.dbPath}: ${err instanceof Error ? err.message : err}`);
        return null;
    }
    const { db, paths: storage, reason: storageReason, quarantinePath } = opened;
    const { dir: architectsDir } = storage;
    const providers = {
        repoRoot: options.repoRoot,
        workflowRuntime: createWorkflowRuntime(undefined, options.repoRoot),
        hookRuntime: createHookRuntime(undefined, options.repoRoot),
        ledger: new PersistentWorkLedger(path.join(architectsDir, 'ledger')),
        relay: nexusNetRelay,
    };
    const operativeActivity = new Map();
    const knownOperatives = new Set();
    const implicitWorklistByTeam = new Map();
    const worklistToTeam = new Map();
    const ward = new ArchitectsWard(db, options.repoRoot, operativeActivity);
    const hasOperative = process.env.ARCHITECTS_OPERATIVE_ID !== 'off';
    const dispatchGovernor = new DispatchGovernor(db);
    const ensureImplicitWorklist = (strikeTeamId) => {
        const existing = implicitWorklistByTeam.get(strikeTeamId);
        if (existing)
            return existing;
        const hydrated = db.prepare(`
      SELECT worklist_id
      FROM architects_blueprints
      WHERE strike_team_id=? AND worklist_id IS NOT NULL
      ORDER BY created_at DESC
      LIMIT 1
    `).get(strikeTeamId);
        if (hydrated?.worklist_id) {
            implicitWorklistByTeam.set(strikeTeamId, hydrated.worklist_id);
            worklistToTeam.set(hydrated.worklist_id, strikeTeamId);
            ward.registerStrikeTeam(strikeTeamId);
            return hydrated.worklist_id;
        }
        const blueprintId = `implicit-blueprint:${strikeTeamId}`;
        const worklistId = `implicit-worklist:${strikeTeamId}`;
        insertBlueprint(db, {
            id: blueprintId,
            strikeTeamId,
            title: `Strike Team ${strikeTeamId}`,
            workflowId: null,
            status: 'active',
            variables: {},
            worklistId,
        });
        insertWorklist(db, {
            id: worklistId,
            blueprintId,
            title: `Strike Team ${strikeTeamId}`,
        });
        implicitWorklistByTeam.set(strikeTeamId, worklistId);
        worklistToTeam.set(worklistId, strikeTeamId);
        ward.registerStrikeTeam(strikeTeamId);
        return worklistId;
    };
    const hydratePersistedState = () => {
        const persistedWorklists = db.prepare(`
      SELECT strike_team_id, worklist_id
      FROM architects_blueprints
      WHERE strike_team_id IS NOT NULL AND worklist_id IS NOT NULL
    `).all();
        persistedWorklists.forEach((row) => {
            if (!row.strike_team_id || !row.worklist_id)
                return;
            implicitWorklistByTeam.set(row.strike_team_id, row.worklist_id);
            worklistToTeam.set(row.worklist_id, row.strike_team_id);
            ward.registerStrikeTeam(row.strike_team_id);
        });
        const workItems = db.prepare(`
      SELECT worklist_id, assigned_operative_id, created_at, merged_at
      FROM architects_work_items
      WHERE assigned_operative_id IS NOT NULL
    `).all();
        const locks = db.prepare(`
      SELECT work_item_id, operative_id, acquired_at
      FROM architects_construction_locks
      WHERE operative_id IS NOT NULL
    `).all();
        const relayRows = db.prepare(`
      SELECT from_operative_id, to_operative_id, sent_at
      FROM architects_relay_messages
    `).all();
        const activityByOperative = new Map();
        const updateActivity = (operativeId, strikeTeamId, timestamp) => {
            const existing = activityByOperative.get(operativeId);
            if (!existing || timestamp >= existing.lastSortieAt) {
                activityByOperative.set(operativeId, {
                    strikeTeamId: strikeTeamId ?? existing?.strikeTeamId ?? null,
                    lastSortieAt: timestamp,
                });
            }
            else if (!existing.strikeTeamId && strikeTeamId) {
                existing.strikeTeamId = strikeTeamId;
            }
        };
        workItems.forEach((row) => {
            const operativeId = row.assigned_operative_id;
            if (!operativeId)
                return;
            const strikeTeamId = row.worklist_id ? worklistToTeam.get(row.worklist_id) ?? null : null;
            updateActivity(operativeId, strikeTeamId, parseTimestamp(row.merged_at ?? row.created_at));
            knownOperatives.add(operativeId);
            if (strikeTeamId) {
                implicitWorklistByTeam.set(strikeTeamId, implicitWorklistByTeam.get(strikeTeamId) ?? row.worklist_id ?? `implicit-worklist:${strikeTeamId}`);
            }
        });
        locks.forEach((row) => {
            if (!row.operative_id)
                return;
            updateActivity(row.operative_id, null, parseTimestamp(row.acquired_at));
            knownOperatives.add(row.operative_id);
        });
        relayRows.forEach((row) => {
            if (row.from_operative_id) {
                knownOperatives.add(row.from_operative_id);
            }
            if (row.to_operative_id) {
                knownOperatives.add(row.to_operative_id);
            }
        });
        for (const [operativeId, activity] of activityByOperative) {
            operativeActivity.set(operativeId, {
                strikeTeamId: activity.strikeTeamId,
                lastSortieAt: activity.lastSortieAt > 0 ? new Date(activity.lastSortieAt).toISOString() : null,
            });
            knownOperatives.add(operativeId);
            if (activity.strikeTeamId) {
                ensureImplicitWorklist(activity.strikeTeamId);
            }
        }
        for (const row of workItems) {
            if (!row.assigned_operative_id)
                continue;
            knownOperatives.add(row.assigned_operative_id);
        }
    };
    hydratePersistedState();
    if (hasOperative)
        ward.start();
    const onOperativeHired = ({ operativeId, strikeTeamId }) => {
        try {
            knownOperatives.add(operativeId);
            operativeActivity.set(operativeId, { strikeTeamId, lastSortieAt: null });
            ensureImplicitWorklist(strikeTeamId);
        }
        catch (err) {
            console.error('[Architects] onOperativeHired failed', operativeId, err);
            nexusEventBus.emit('architects.event.failed', { event: 'operative.hired', operativeId, error: String(err) });
        }
    };
    const onMissionAssigned = ({ operativeId, missionId, title }) => {
        try {
            const activity = operativeActivity.get(operativeId);
            const strikeTeamId = activity?.strikeTeamId ?? null;
            if (!strikeTeamId)
                return;
            const worklistId = ensureImplicitWorklist(strikeTeamId);
            upsertWorkItem(db, {
                id: missionId,
                worklistId,
                title,
                status: 'todo',
                assignedOperativeId: operativeId,
                constructionLockId: null,
                dependsOn: [],
                branch: null,
                mergedAt: null,
                createdAt: new Date().toISOString(),
            });
        }
        catch (err) {
            console.error('[Architects] onMissionAssigned failed', missionId, err);
            nexusEventBus.emit('architects.event.failed', { event: 'mission.assigned', operativeId, missionId, error: String(err) });
        }
    };
    const onSortieCompleted = ({ operativeId, missionId, status }) => {
        try {
            const activity = operativeActivity.get(operativeId);
            if (activity) {
                activity.lastSortieAt = new Date().toISOString();
                operativeActivity.set(operativeId, activity);
            }
            if (!missionId)
                return;
            updateWorkItemStatus(db, missionId, status === 'completed' ? 'done' : status === 'failed' ? 'failed' : 'blocked');
        }
        catch (err) {
            console.error('[Architects] onSortieCompleted failed', missionId, err);
            nexusEventBus.emit('architects.event.failed', { event: 'sortie.completed', operativeId, missionId: missionId ?? undefined, error: String(err) });
        }
    };
    const onStriketeamDeployed = ({ strikeTeamId }) => {
        try {
            ensureImplicitWorklist(strikeTeamId);
        }
        catch (err) {
            console.error('[Architects] onStriketeamDeployed failed', strikeTeamId, err);
            nexusEventBus.emit('architects.event.failed', { event: 'striketeam.deployed', strikeTeamId, error: String(err) });
        }
    };
    const onStriketeamFinished = ({ strikeTeamId, intent, outcome }) => {
        try {
            if (intent === 'mutate' && outcome !== 'cancelled' && outcome !== 'failed')
                return;
            const worklistId = implicitWorklistByTeam.get(strikeTeamId);
            if (!worklistId)
                return;
            db.prepare(`
        UPDATE architects_work_items SET status='cancelled'
        WHERE worklist_id=? AND status='todo'
      `).run(worklistId);
        }
        catch (err) {
            console.error('[Architects] onStriketeamFinished failed', strikeTeamId, err);
            nexusEventBus.emit('architects.event.failed', { event: 'striketeam.finished', strikeTeamId, error: String(err) });
        }
    };
    const onStanddown = () => setConvergencePaused(true);
    const onResumed = () => setConvergencePaused(false);
    const unsubOperative = nexusEventBus.on('synapse.operative.hired', onOperativeHired);
    const unsubMission = nexusEventBus.on('synapse.mission.assigned', onMissionAssigned);
    const unsubSortie = nexusEventBus.on('synapse.sortie.completed', onSortieCompleted);
    const unsubTeam = nexusEventBus.on('synapse.striketeam.deployed', onStriketeamDeployed);
    const unsubFinished = nexusEventBus.on('synapse.striketeam.finished', onStriketeamFinished);
    const unsubStanddown = nexusEventBus.on('synapse.compaction.standdown', onStanddown);
    const unsubResumed = nexusEventBus.on('synapse.compaction.resumed', onResumed);
    nexusEventBus.emit('architects.ready', { version: '5.0.0' });
    console.error('[Architects] Initialized. Operative mode active.');
    return {
        db,
        providers,
        instantiateBlueprint: (input) => instantiateBlueprint(db, providers, input),
        getWorklist: (worklistId) => ({
            worklist: getWorklist(db, worklistId),
            items: getWorkItemsForWorklist(db, worklistId),
        }),
        getWorklistForStrikeTeam: (strikeTeamId) => ensureImplicitWorklist(strikeTeamId),
        claimWorkItem: async (workItemId, operativeId) => {
            dispatchGovernor.dispatch(operativeId, workItemId);
            return acquireConstructionLock(operativeId, workItemId, db, providers);
        },
        renewWorkItemClaim: async (workItemId, operativeId, leaseDurationMs) => renewConstructionLock(operativeId, workItemId, db, leaseDurationMs ? { leaseDurationMs } : {}),
        completeWorkItem: async (workItemId, operativeId, status, branch) => {
            await releaseConstructionLock(operativeId, workItemId, db, status);
            const item = updateWorkItemStatus(db, workItemId, status, branch ?? null);
            if (item) {
                providers.ledger.record(`architects-${workItemId}`, item, `Architects work item ${workItemId} -> ${status}`);
                if (status === 'blocked') {
                    nexusEventBus.emit('architects.workitem.blocked', {
                        workItemId,
                        operativeId,
                        reason: 'marked-blocked',
                    });
                }
                else {
                    nexusEventBus.emit('architects.workitem.completed', {
                        workItemId,
                        operativeId,
                        status,
                    });
                }
            }
            dispatchGovernor.complete();
            return item;
        },
        sendRelay: (input) => sendRelay(db, providers, knownOperatives, input),
        getRelayInbox: (input = {}) => getRelayInbox(db, input),
        getSentinelReport: async (strikeTeamId) => {
            ward.registerStrikeTeam(strikeTeamId);
            const sentinel = new ArchitectsSentinel(db, strikeTeamId, options.repoRoot, operativeActivity);
            return sentinel.patrol();
        },
        runConvergenceQueue: (worklistId) => runConvergenceQueue(db, options.repoRoot, worklistId),
        getDispatchStatus: () => dispatchGovernor.getStatus(),
        getWardEscalations: () => ward.getEscalations(),
        getStatus: () => createEngineHealthStatus({
            name: 'architects',
            available: true,
            ready: true,
            repoRoot: options.repoRoot,
            requestedStorageRoot: storage.requestedRoot,
            storageRoot: storage.storageRoot,
            dbPath: storage.dbPath,
            fallbackApplied: storage.fallbackApplied,
            reason: storageReason ?? storage.fallbackReason,
            quarantinePath,
        }),
        stop: () => {
            if (hasOperative)
                ward.stop();
            unsubOperative();
            unsubMission();
            unsubSortie();
            unsubTeam();
            unsubFinished();
            unsubStanddown();
            unsubResumed();
            db.close();
        },
    };
}
