import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import Database from 'better-sqlite3';
import { runMigrations } from '../../db/migrations.js';
import { createPipelineTasks, markTaskExecuting, markTaskDone, markTaskFailed, markAllTasksDone, markAllTasksFailed, getActiveActivityId, markTaskExecutingByPosition, markTaskDoneByPosition, } from '../pipeline-task-service.js';
describe('pipeline-task-service', () => {
    let db;
    beforeEach(() => {
        db = new Database(':memory:');
        runMigrations(db);
        // Create a squad_activities row for FK
        db.prepare("INSERT INTO squad_activities (id, squad_code, started_at, status, triggered_by, created_at) VALUES (?, ?, datetime('now'), 'running', 'chat', datetime('now'))").run('activity-1', 'test-squad');
    });
    afterEach(() => {
        db.close();
    });
    describe('createPipelineTasks', () => {
        it('should create one task per step with status planned', () => {
            const tasks = createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'Research', agentId: 'researcher', agentName: 'Researcher Agent' },
                    { id: 'step-02', label: 'Write', agentId: 'writer', agentName: 'Writer Agent' },
                ],
            });
            expect(tasks).toHaveLength(2);
            expect(tasks[0].status).toBe('planned');
            expect(tasks[0].step_id).toBe('step-01');
            expect(tasks[0].agent_name).toBe('Researcher Agent');
            expect(tasks[0].activity_id).toBe('activity-1');
            expect(tasks[0].triggered_by).toBe('chat');
            expect(tasks[0].position).toBe(0);
            expect(tasks[1].position).toBe(1);
        });
        it('should handle checkpoint steps with null agent', () => {
            const tasks = createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'schedule',
                steps: [
                    { id: 'step-01', label: 'Research', agentId: 'researcher', agentName: 'Researcher' },
                    { id: 'step-02', label: 'Approval gate', agentId: null, agentName: null, type: 'checkpoint' },
                    { id: 'step-03', label: 'Write', agentId: 'writer', agentName: 'Writer' },
                ],
            });
            expect(tasks).toHaveLength(3);
            expect(tasks[1].agent_id).toBeNull();
            expect(tasks[1].agent_name).toBeNull();
            expect(tasks[1].step_id).toBe('step-02');
        });
        it('should persist tasks in the database', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'Research', agentId: 'r1', agentName: 'R1' },
                ],
            });
            const rows = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1'").all();
            expect(rows).toHaveLength(1);
            expect(rows[0].title).toBe('Research');
            expect(rows[0].squad_code).toBe('test-squad');
        });
    });
    describe('markTaskExecuting', () => {
        it('should set status to executing and started_at', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'Work', agentId: 'a1', agentName: 'A1' }],
            });
            markTaskExecuting(db, { activityId: 'activity-1', stepId: 'step-01' });
            const task = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' AND step_id = 'step-01'").get();
            expect(task.status).toBe('executing');
            expect(task.started_at).toBeTruthy();
        });
    });
    describe('markTaskDone', () => {
        it('should set status to done with result', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'Work', agentId: 'a1', agentName: 'A1' }],
            });
            markTaskDone(db, { activityId: 'activity-1', stepId: 'step-01', result: 'Report done' });
            const task = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' AND step_id = 'step-01'").get();
            expect(task.status).toBe('done');
            expect(task.result).toBe('Report done');
            expect(task.finished_at).toBeTruthy();
        });
    });
    describe('markTaskFailed', () => {
        it('should set status to failed with error in result', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'Work', agentId: 'a1', agentName: 'A1' }],
            });
            markTaskFailed(db, { activityId: 'activity-1', stepId: 'step-01', error: 'Timeout' });
            const task = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' AND step_id = 'step-01'").get();
            expect(task.status).toBe('failed');
            expect(task.result).toBe('Timeout');
            expect(task.finished_at).toBeTruthy();
        });
    });
    describe('markAllTasksDone', () => {
        it('should bulk-transition planned and executing tasks to done', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'schedule',
                steps: [
                    { id: 'step-01', label: 'A', agentId: 'a1', agentName: 'A1' },
                    { id: 'step-02', label: 'B', agentId: 'a2', agentName: 'A2' },
                    { id: 'step-03', label: 'C', agentId: 'a3', agentName: 'A3' },
                ],
            });
            // Move one to executing to verify it also gets transitioned
            markTaskExecuting(db, { activityId: 'activity-1', stepId: 'step-01' });
            markAllTasksDone(db, { activityId: 'activity-1' });
            const tasks = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' ORDER BY position").all();
            expect(tasks).toHaveLength(3);
            expect(tasks.every((t) => t.status === 'done')).toBe(true);
            expect(tasks.every((t) => t.finished_at !== null)).toBe(true);
        });
        it('should not affect tasks from other activities', () => {
            // Create activity-2
            db.prepare("INSERT INTO squad_activities (id, squad_code, started_at, status, triggered_by, created_at) VALUES (?, ?, datetime('now'), 'running', 'chat', datetime('now'))").run('activity-2', 'other-squad');
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'A', agentId: 'a1', agentName: 'A1' }],
            });
            createPipelineTasks(db, {
                squadCode: 'other-squad',
                activityId: 'activity-2',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'B', agentId: 'b1', agentName: 'B1' }],
            });
            markAllTasksDone(db, { activityId: 'activity-1' });
            const otherTask = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-2'").get();
            expect(otherTask.status).toBe('planned');
        });
    });
    describe('getActiveActivityId', () => {
        it('should return the running activity id for a squad', () => {
            const id = getActiveActivityId(db, 'test-squad');
            expect(id).toBe('activity-1');
        });
        it('should return null when no running activity exists', () => {
            db.prepare("UPDATE squad_activities SET status = 'completed' WHERE id = 'activity-1'").run();
            const id = getActiveActivityId(db, 'test-squad');
            expect(id).toBeNull();
        });
        it('should return the most recent running activity', () => {
            db.prepare("INSERT INTO squad_activities (id, squad_code, started_at, status, triggered_by, created_at) VALUES (?, ?, datetime('now', '+1 second'), 'running', 'schedule', datetime('now'))").run('activity-newer', 'test-squad');
            const id = getActiveActivityId(db, 'test-squad');
            expect(id).toBe('activity-newer');
        });
    });
    describe('markTaskExecutingByPosition', () => {
        it('should mark a planned task as executing by position', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'First', agentId: 'a1', agentName: 'A1' },
                    { id: 'step-02', label: 'Second', agentId: 'a2', agentName: 'A2' },
                ],
            });
            markTaskExecutingByPosition(db, { activityId: 'activity-1', position: 1 });
            const tasks = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' ORDER BY position").all();
            expect(tasks[0].status).toBe('planned');
            expect(tasks[1].status).toBe('executing');
            expect(tasks[1].started_at).toBeTruthy();
        });
        it('should not change a task that is already executing', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'Work', agentId: 'a1', agentName: 'A1' }],
            });
            markTaskExecuting(db, { activityId: 'activity-1', stepId: 'step-01' });
            const before = db.prepare("SELECT started_at FROM kanban_tasks WHERE activity_id = 'activity-1'").get();
            // Calling by position again should not update (already executing, WHERE clause filters)
            markTaskExecutingByPosition(db, { activityId: 'activity-1', position: 0 });
            const after = db.prepare("SELECT started_at FROM kanban_tasks WHERE activity_id = 'activity-1'").get();
            expect(after.started_at).toBe(before.started_at);
        });
    });
    describe('markTaskDoneByPosition', () => {
        it('should mark an executing task as done by position', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'First', agentId: 'a1', agentName: 'A1' },
                    { id: 'step-02', label: 'Second', agentId: 'a2', agentName: 'A2' },
                ],
            });
            markTaskExecutingByPosition(db, { activityId: 'activity-1', position: 0 });
            markTaskDoneByPosition(db, { activityId: 'activity-1', position: 0 });
            const tasks = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' ORDER BY position").all();
            expect(tasks[0].status).toBe('done');
            expect(tasks[0].finished_at).toBeTruthy();
            expect(tasks[1].status).toBe('planned');
        });
        it('should not mark a planned task as done (must be executing first)', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [{ id: 'step-01', label: 'Work', agentId: 'a1', agentName: 'A1' }],
            });
            markTaskDoneByPosition(db, { activityId: 'activity-1', position: 0 });
            const task = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1'").get();
            expect(task.status).toBe('planned'); // unchanged
        });
    });
    describe('markAllTasksFailed', () => {
        it('should bulk-transition planned and executing tasks to failed', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'A', agentId: 'a1', agentName: 'A1' },
                    { id: 'step-02', label: 'B', agentId: 'a2', agentName: 'A2' },
                ],
            });
            // Manually move first to executing
            markTaskExecuting(db, { activityId: 'activity-1', stepId: 'step-01' });
            markAllTasksFailed(db, { activityId: 'activity-1', error: 'Pipeline crashed' });
            const tasks = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' ORDER BY position").all();
            expect(tasks.every((t) => t.status === 'failed')).toBe(true);
            expect(tasks.every((t) => t.result === 'Pipeline crashed')).toBe(true);
        });
        it('should not affect already-done tasks', () => {
            createPipelineTasks(db, {
                squadCode: 'test-squad',
                activityId: 'activity-1',
                triggeredBy: 'chat',
                steps: [
                    { id: 'step-01', label: 'A', agentId: 'a1', agentName: 'A1' },
                    { id: 'step-02', label: 'B', agentId: 'a2', agentName: 'A2' },
                ],
            });
            // Mark first as done with a result
            markTaskDone(db, { activityId: 'activity-1', stepId: 'step-01', result: 'Success output' });
            markAllTasksFailed(db, { activityId: 'activity-1', error: 'Crashed' });
            const tasks = db.prepare("SELECT * FROM kanban_tasks WHERE activity_id = 'activity-1' ORDER BY position").all();
            expect(tasks[0].status).toBe('done');
            expect(tasks[0].result).toBe('Success output'); // result preserved, not overwritten
            expect(tasks[1].status).toBe('failed');
            expect(tasks[1].result).toBe('Crashed');
        });
    });
});
//# sourceMappingURL=pipeline-task-service.test.js.map