import { mkdtemp, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { openDatabase, closeDatabase } from "../database.js";
import { createProject } from "../repositories/projects.js";
import { cleanupOldTerminalSessions, startCleanupJob, stopCleanupJob } from "../cleanup.js";
vi.mock("../../utils/logger.js", () => ({
    logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
describe("database cleanup", () => {
    let tempDir;
    let dbPath;
    let projectId;
    beforeEach(async () => {
        tempDir = await mkdtemp(join(tmpdir(), "pocketcoder-cleanup-test-"));
        dbPath = join(tempDir, "daemon.db");
        closeDatabase();
        openDatabase(dbPath);
        const project = createProject("test-project", "/tmp/test-project");
        projectId = project.project_id;
    });
    afterEach(async () => {
        stopCleanupJob();
        closeDatabase();
        await rm(tempDir, { recursive: true, force: true });
    });
    it("should remove closed sessions older than 7 days", () => {
        const db = openDatabase();
        // Insert a closed session with an old timestamp
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, created_at, closed_at) VALUES (?, ?, ?, datetime('now', '-8 days'), datetime('now', '-8 days'))").run("old-session", projectId, 1234);
        // Insert a recent closed session
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, closed_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP)").run("new-session", projectId, 5678);
        const deleted = cleanupOldTerminalSessions();
        expect(deleted).toBeGreaterThanOrEqual(1);
        const oldRow = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("old-session");
        expect(oldRow).toBeUndefined();
        const newRow = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("new-session");
        expect(newRow).toBeDefined();
    });
    it("should not remove active sessions", () => {
        const db = openDatabase();
        // Insert an old but still active session (no closed_at)
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, created_at) VALUES (?, ?, ?, datetime('now', '-30 days'))").run("active-old", projectId, 1111);
        // Insert a recent active session
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid) VALUES (?, ?, ?)").run("active-new", projectId, 2222);
        const deleted = cleanupOldTerminalSessions();
        expect(deleted).toBe(0);
        const oldActive = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("active-old");
        expect(oldActive).toBeDefined();
        const newActive = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("active-new");
        expect(newActive).toBeDefined();
    });
    it("should keep only 1000 most recent sessions", () => {
        const db = openDatabase();
        const insert = db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, closed_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP)");
        const insertMany = db.transaction((count) => {
            for (let i = 0; i < count; i++) {
                insert.run(`session-${i}`, projectId, i);
            }
        });
        // Insert 1005 closed sessions
        insertMany(1005);
        const countBefore = db.prepare("SELECT COUNT(*) as count FROM terminal_sessions").get().count;
        expect(countBefore).toBe(1005);
        cleanupOldTerminalSessions();
        const countAfter = db.prepare("SELECT COUNT(*) as count FROM terminal_sessions").get().count;
        expect(countAfter).toBe(1000);
    });
    it("should preserve active sessions when trimming to 1000 limit", () => {
        const db = openDatabase();
        // Insert 1005 closed sessions
        const insertClosed = db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, closed_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP)");
        const insertClosedMany = db.transaction((count) => {
            for (let i = 0; i < count; i++) {
                insertClosed.run(`closed-${i}`, projectId, i);
            }
        });
        insertClosedMany(1005);
        // Insert 3 active sessions (no closed_at)
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid) VALUES (?, ?, ?)").run("active-1", projectId, 9001);
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid) VALUES (?, ?, ?)").run("active-2", projectId, 9002);
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid) VALUES (?, ?, ?)").run("active-3", projectId, 9003);
        cleanupOldTerminalSessions();
        // Active sessions should all still exist
        for (const id of ["active-1", "active-2", "active-3"]) {
            const row = db.prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?").get(id);
            expect(row).toBeDefined();
        }
        // Total should be 1000 closed + 3 active = 1003
        const totalCount = db.prepare("SELECT COUNT(*) as count FROM terminal_sessions").get().count;
        expect(totalCount).toBe(1003);
    });
    it("should start cleanup job and run immediately", () => {
        vi.useFakeTimers();
        const db = openDatabase();
        // Insert an old closed session to verify cleanup runs
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, created_at, closed_at) VALUES (?, ?, ?, datetime('now', '-10 days'), datetime('now', '-10 days'))").run("startup-old", projectId, 1234);
        startCleanupJob();
        // The old session should have been cleaned up immediately
        const row = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("startup-old");
        expect(row).toBeUndefined();
        vi.useRealTimers();
    });
    it("should run cleanup periodically every 6 hours", () => {
        vi.useFakeTimers();
        const db = openDatabase();
        startCleanupJob();
        // Insert an old closed session after startup cleanup
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, created_at, closed_at) VALUES (?, ?, ?, datetime('now', '-10 days'), datetime('now', '-10 days'))").run("periodic-old", projectId, 5678);
        // Verify it exists before the interval fires
        let row = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("periodic-old");
        expect(row).toBeDefined();
        // Advance past 6 hours
        vi.advanceTimersByTime(6 * 60 * 60 * 1000);
        // Should have been cleaned up
        row = db.prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?").get("periodic-old");
        expect(row).toBeUndefined();
        vi.useRealTimers();
    });
    it("should stop cleanup job", () => {
        vi.useFakeTimers();
        const db = openDatabase();
        startCleanupJob();
        stopCleanupJob();
        // Insert an old closed session after stopping
        db.prepare("INSERT INTO terminal_sessions (terminal_id, project_id, pid, created_at, closed_at) VALUES (?, ?, ?, datetime('now', '-10 days'), datetime('now', '-10 days'))").run("after-stop", projectId, 9999);
        // Advance time — cleanup should NOT run since we stopped it
        vi.advanceTimersByTime(6 * 60 * 60 * 1000);
        const row = db
            .prepare("SELECT * FROM terminal_sessions WHERE terminal_id = ?")
            .get("after-stop");
        expect(row).toBeDefined();
        vi.useRealTimers();
    });
    it("should handle cleanup when no sessions exist", () => {
        const deleted = cleanupOldTerminalSessions();
        expect(deleted).toBe(0);
    });
});
//# sourceMappingURL=cleanup.test.js.map