import { workflowSchema } from './schema.js';
import { summarizeError } from '../../utils/error-display.js';
export const WORKFLOW_DEFINITIONS = Object.freeze(JSON.parse(JSON.stringify({
    wrfc: {
        name: 'WRFC Loop',
        states: ['gather', 'plan', 'apply', 'review', 'revision', 'complete'],
        transitions: {
            gather: ['plan'],
            plan: ['apply'],
            apply: ['review'],
            review: ['revision', 'complete'],
            revision: ['apply'],
        },
        description: 'Full work-review-fix cycle',
    },
    fix_loop: {
        name: 'Fix Loop',
        states: ['apply', 'test', 'verify', 'complete'],
        transitions: { apply: ['test'], test: ['verify', 'apply'], verify: ['complete'] },
        description: 'Apply fix, test, verify',
    },
    test_then_fix: {
        name: 'Test Then Fix',
        states: ['test', 'fix', 'verify', 'complete'],
        transitions: { test: ['fix'], fix: ['verify'], verify: ['complete', 'fix'] },
        description: 'Run tests, fix failures, verify',
    },
    review_only: {
        name: 'Review Only',
        states: ['review', 'complete'],
        transitions: { review: ['complete'] },
        description: 'Skip to review phase',
    },
})));
const WORKFLOW_EVICT_AFTER_MS = 60 * 60 * 1000; // 1 hour
const WORKFLOW_MAX_COMPLETED = 50;
export class WorkflowManager {
    workflows = new Map();
    start(definition, task) {
        const def = WORKFLOW_DEFINITIONS[definition];
        if (!def) {
            throw new Error(`Unknown workflow definition: ${definition}`);
        }
        const id = `wf-${crypto.randomUUID().slice(0, 8)}`;
        const instance = {
            id,
            definition,
            currentState: def.states[0],
            task,
            startedAt: Date.now(),
            transitions: 0,
            context: {},
        };
        this.workflows.set(id, instance);
        return instance;
    }
    getStatus(id) {
        return this.workflows.get(id) ?? null;
    }
    transition(id, targetState) {
        const instance = this.workflows.get(id);
        if (!instance) {
            return { success: false, error: `Workflow not found: ${id}` };
        }
        if (instance.cancelled) {
            return { success: false, error: 'Workflow is cancelled' };
        }
        const def = WORKFLOW_DEFINITIONS[instance.definition];
        const allowed = def?.transitions[instance.currentState] ?? [];
        if (!allowed.includes(targetState)) {
            return {
                success: false,
                error: `Invalid transition: ${instance.currentState} -> ${targetState}. Allowed: [${allowed.join(', ')}]`,
            };
        }
        instance.currentState = targetState;
        instance.transitions += 1;
        if (targetState === 'complete') {
            instance.completedAt = Date.now();
        }
        return { success: true };
    }
    cancel(id) {
        const instance = this.workflows.get(id);
        if (!instance)
            return false;
        instance.cancelled = true;
        instance.completedAt = Date.now();
        return true;
    }
    list() {
        const now = Date.now();
        // Evict old completed/cancelled workflows
        for (const [id, wf] of this.workflows) {
            if (wf.completedAt !== undefined && now - wf.completedAt > WORKFLOW_EVICT_AFTER_MS) {
                this.workflows.delete(id);
            }
        }
        // Cap completed workflows at max
        const all = Array.from(this.workflows.values());
        const completed = all.filter(w => w.completedAt !== undefined);
        if (completed.length > WORKFLOW_MAX_COMPLETED) {
            completed.sort((a, b) => (a.completedAt ?? 0) - (b.completedAt ?? 0));
            const toEvict = completed.slice(0, completed.length - WORKFLOW_MAX_COMPLETED);
            for (const wf of toEvict)
                this.workflows.delete(wf.id);
        }
        return Array.from(this.workflows.values());
    }
}
export class TriggerManager {
    triggers = new Map();
    add(def) {
        const id = `trg-${crypto.randomUUID().slice(0, 8)}`;
        const trigger = {
            id,
            event: def.event,
            condition: def.condition,
            action: def.action,
            enabled: true,
        };
        this.triggers.set(id, trigger);
        return trigger;
    }
    remove(id) {
        return this.triggers.delete(id);
    }
    enable(id) {
        const trigger = this.triggers.get(id);
        if (!trigger)
            return false;
        trigger.enabled = true;
        return true;
    }
    disable(id) {
        const trigger = this.triggers.get(id);
        if (!trigger)
            return false;
        trigger.enabled = false;
        return true;
    }
    list() {
        return Array.from(this.triggers.values());
    }
}
/**
 * Parse an interval string like '30s', '5m', '1h' to milliseconds.
 * Returns null if the format is unrecognised.
 */
export function parseInterval(interval) {
    const match = interval.trim().match(/^(\d+(?:\.\d+)?)(s|m|h|d)$/);
    if (!match)
        return null;
    const value = parseFloat(match[1]);
    switch (match[2]) {
        case 's': return value * 1_000;
        case 'm': return value * 60_000;
        case 'h': return value * 3_600_000;
        case 'd': return value * 86_400_000;
        default: return null;
    }
}
export class ScheduleManager {
    schedules = new Map();
    /** Timer IDs keyed by schedule name */
    timers = new Map();
    /** Spawned process handles tracked for cleanup in destroy() */
    spawnedProcs = [];
    add(name, interval, command) {
        // Clear existing timer if re-adding
        this._clearTimer(name);
        const now = Date.now();
        const intervalMs = parseInterval(interval);
        const entry = {
            name,
            interval,
            command,
            enabled: true,
            nextRun: intervalMs !== null ? now + intervalMs : undefined,
        };
        this.schedules.set(name, entry);
        if (intervalMs !== null) {
            const timer = setInterval(() => this._tick(name), intervalMs);
            this.timers.set(name, timer);
        }
        return entry;
    }
    remove(name) {
        this._clearTimer(name);
        return this.schedules.delete(name);
    }
    enable(name) {
        const entry = this.schedules.get(name);
        if (!entry)
            return false;
        if (entry.enabled)
            return true;
        entry.enabled = true;
        // Restart timer
        const intervalMs = parseInterval(entry.interval);
        if (intervalMs !== null && !this.timers.has(name)) {
            entry.nextRun = Date.now() + intervalMs;
            const timer = setInterval(() => this._tick(name), intervalMs);
            this.timers.set(name, timer);
        }
        return true;
    }
    disable(name) {
        const entry = this.schedules.get(name);
        if (!entry)
            return false;
        entry.enabled = false;
        this._clearTimer(name);
        entry.nextRun = undefined;
        return true;
    }
    list() {
        return Array.from(this.schedules.values());
    }
    /** Stop all timers and clear state when the manager is disposed. */
    destroy() {
        for (const name of this.timers.keys()) {
            this._clearTimer(name);
        }
        this.schedules.clear();
        // Kill any still-running spawned processes
        for (const { proc } of this.spawnedProcs) {
            try {
                proc.kill();
            }
            catch { /* non-fatal */ }
        }
        this.spawnedProcs = [];
    }
    /** Execute a schedule tick: run the command and update lastRun/nextRun. */
    _tick(name) {
        const entry = this.schedules.get(name);
        if (!entry || !entry.enabled)
            return;
        const now = Date.now();
        entry.lastRun = now;
        const intervalMs = parseInterval(entry.interval);
        entry.nextRun = intervalMs !== null ? now + intervalMs : undefined;
        // Parse command string
        const parts = entry.command.split(/\s+/).filter(Boolean);
        if (parts.length === 0)
            return;
        try {
            const proc = Bun.spawn(parts, {
                env: { ...process.env, GV_SCHEDULE_NAME: name },
                stdout: 'ignore',
                stderr: 'ignore',
            });
            const pid = proc.pid;
            this.spawnedProcs.push({ pid, proc });
            proc.exited.then(() => {
                // Remove from tracking once the process has exited
                const idx = this.spawnedProcs.findIndex(p => p.pid === pid);
                if (idx !== -1)
                    this.spawnedProcs.splice(idx, 1);
            }).catch(() => { });
        }
        catch {
            // Spawn failure is non-fatal for the scheduler
        }
    }
    _clearTimer(name) {
        const timer = this.timers.get(name);
        if (timer !== undefined) {
            clearInterval(timer);
            this.timers.delete(name);
        }
    }
}
export function createWorkflowServices() {
    return {
        workflowManager: new WorkflowManager(),
        triggerManager: new TriggerManager(),
        scheduleManager: new ScheduleManager(),
    };
}
// ---------------------------------------------------------------------------
// Tool implementation
// ---------------------------------------------------------------------------
export function createWorkflowTool(services) {
    return {
        definition: workflowSchema,
        async execute(args) {
            try {
                if (!args.mode || typeof args.mode !== 'string') {
                    return { success: false, error: 'Missing required "mode" field' };
                }
                const input = args;
                const wm = services.workflowManager;
                const tm = services.triggerManager;
                const sm = services.scheduleManager;
                switch (input.mode) {
                    case 'start': {
                        if (!input.definition) {
                            return { success: false, error: 'mode "start" requires "definition"' };
                        }
                        if (!input.task) {
                            return { success: false, error: 'mode "start" requires "task"' };
                        }
                        if (!WORKFLOW_DEFINITIONS[input.definition]) {
                            return { success: false, error: `Unknown workflow definition: ${input.definition}` };
                        }
                        const instance = wm.start(input.definition, input.task);
                        return { success: true, output: JSON.stringify(instance) };
                    }
                    case 'status': {
                        if (!input.workflowId) {
                            return { success: false, error: 'mode "status" requires "workflowId"' };
                        }
                        const instance = wm.getStatus(input.workflowId);
                        if (!instance) {
                            return { success: false, error: `Workflow not found: ${input.workflowId}` };
                        }
                        return { success: true, output: JSON.stringify(instance) };
                    }
                    case 'transition': {
                        if (!input.workflowId) {
                            return { success: false, error: 'mode "transition" requires "workflowId"' };
                        }
                        if (!input.targetState) {
                            return { success: false, error: 'mode "transition" requires "targetState"' };
                        }
                        const result = wm.transition(input.workflowId, input.targetState);
                        if (!result.success) {
                            return { success: false, error: result.error };
                        }
                        const updated = wm.getStatus(input.workflowId);
                        return { success: true, output: JSON.stringify(updated) };
                    }
                    case 'cancel': {
                        if (!input.workflowId) {
                            return { success: false, error: 'mode "cancel" requires "workflowId"' };
                        }
                        const cancelled = wm.cancel(input.workflowId);
                        if (!cancelled) {
                            return { success: false, error: `Workflow not found: ${input.workflowId}` };
                        }
                        return { success: true, output: JSON.stringify({ cancelled: true, workflowId: input.workflowId }) };
                    }
                    case 'list': {
                        const workflows = wm.list();
                        return { success: true, output: JSON.stringify({ workflows, count: workflows.length }) };
                    }
                    case 'triggers': {
                        const action = input.triggerAction ?? 'list';
                        switch (action) {
                            case 'list': {
                                const triggers = tm.list();
                                return { success: true, output: JSON.stringify({ triggers, count: triggers.length }) };
                            }
                            case 'add': {
                                if (!input.triggerDefinition) {
                                    return { success: false, error: 'triggers/add requires "triggerDefinition"' };
                                }
                                const trigger = tm.add(input.triggerDefinition);
                                return { success: true, output: JSON.stringify(trigger) };
                            }
                            case 'remove': {
                                if (!input.triggerId) {
                                    return { success: false, error: 'triggers/remove requires "triggerId"' };
                                }
                                const removed = tm.remove(input.triggerId);
                                return { success: true, output: JSON.stringify({ removed, triggerId: input.triggerId }) };
                            }
                            case 'enable': {
                                if (!input.triggerId) {
                                    return { success: false, error: 'triggers/enable requires "triggerId"' };
                                }
                                const enabled = tm.enable(input.triggerId);
                                if (!enabled) {
                                    return { success: false, error: `Trigger not found: ${input.triggerId}` };
                                }
                                return { success: true, output: JSON.stringify({ enabled: true, triggerId: input.triggerId }) };
                            }
                            case 'disable': {
                                if (!input.triggerId) {
                                    return { success: false, error: 'triggers/disable requires "triggerId"' };
                                }
                                const disabled = tm.disable(input.triggerId);
                                if (!disabled) {
                                    return { success: false, error: `Trigger not found: ${input.triggerId}` };
                                }
                                return { success: true, output: JSON.stringify({ disabled: true, triggerId: input.triggerId }) };
                            }
                            default: {
                                const exhaustive = action;
                                return { success: false, error: `Unknown triggerAction: ${exhaustive}` };
                            }
                        }
                    }
                    case 'schedule': {
                        const action = input.scheduleAction ?? 'list';
                        switch (action) {
                            case 'list': {
                                const schedules = sm.list();
                                return { success: true, output: JSON.stringify({ schedules, count: schedules.length }) };
                            }
                            case 'add': {
                                if (!input.scheduleName) {
                                    return { success: false, error: 'schedule/add requires "scheduleName"' };
                                }
                                if (!input.scheduleInterval) {
                                    return { success: false, error: 'schedule/add requires "scheduleInterval"' };
                                }
                                if (!input.scheduleCommand) {
                                    return { success: false, error: 'schedule/add requires "scheduleCommand"' };
                                }
                                const entry = sm.add(input.scheduleName, input.scheduleInterval, input.scheduleCommand);
                                return { success: true, output: JSON.stringify(entry) };
                            }
                            case 'remove': {
                                if (!input.scheduleName) {
                                    return { success: false, error: 'schedule/remove requires "scheduleName"' };
                                }
                                const removed = sm.remove(input.scheduleName);
                                return { success: true, output: JSON.stringify({ removed, scheduleName: input.scheduleName }) };
                            }
                            default: {
                                const exhaustive = action;
                                return { success: false, error: `Unknown scheduleAction: ${exhaustive}` };
                            }
                        }
                    }
                    default: {
                        const exhaustive = input.mode;
                        return { success: false, error: `Unknown mode: ${exhaustive}` };
                    }
                }
            }
            catch (err) {
                return {
                    success: false,
                    error: summarizeError(err),
                };
            }
        },
    };
}
