import assert from 'node:assert/strict';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import Database from 'better-sqlite3';
import { closeConnection, initializeDatabase, sessionsDb } from '../../../modules/database/index.js';
import { OpenCodeSessionSynchronizer } from '../../../modules/providers/list/opencode/opencode-session-synchronizer.provider.js';
import { OpenCodeSessionsProvider } from '../../../modules/providers/list/opencode/opencode-sessions.provider.js';
const patchHomeDir = (nextHomeDir) => {
    const original = os.homedir;
    os.homedir = () => nextHomeDir;
    return () => {
        os.homedir = original;
    };
};
async function withIsolatedDatabase(runTest) {
    const previousDatabasePath = process.env.DATABASE_PATH;
    const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'opencode-provider-db-'));
    const databasePath = path.join(tempDirectory, 'auth.db');
    closeConnection();
    process.env.DATABASE_PATH = databasePath;
    await initializeDatabase();
    try {
        await runTest();
    }
    finally {
        closeConnection();
        if (previousDatabasePath === undefined) {
            delete process.env.DATABASE_PATH;
        }
        else {
            process.env.DATABASE_PATH = previousDatabasePath;
        }
        await rm(tempDirectory, { recursive: true, force: true });
    }
}
const createOpenCodeDatabase = async (homeDir, workspacePath) => {
    const dataDir = path.join(homeDir, '.local', 'share', 'opencode');
    await mkdir(dataDir, { recursive: true });
    const db = new Database(path.join(dataDir, 'opencode.db'));
    try {
        db.exec(`
      CREATE TABLE project (
        id TEXT PRIMARY KEY,
        worktree TEXT NOT NULL,
        vcs TEXT,
        name TEXT,
        icon_url TEXT,
        icon_color TEXT,
        time_created INTEGER NOT NULL,
        time_updated INTEGER NOT NULL,
        time_initialized INTEGER,
        sandboxes TEXT NOT NULL,
        commands TEXT,
        icon_url_override TEXT
      );

      CREATE TABLE session (
        id TEXT PRIMARY KEY,
        project_id TEXT NOT NULL,
        parent_id TEXT,
        slug TEXT NOT NULL,
        directory TEXT NOT NULL,
        title TEXT NOT NULL,
        version TEXT NOT NULL,
        share_url TEXT,
        summary_additions INTEGER,
        summary_deletions INTEGER,
        summary_files INTEGER,
        summary_diffs TEXT,
        revert TEXT,
        permission TEXT,
        time_created INTEGER NOT NULL,
        time_updated INTEGER NOT NULL,
        time_compacting INTEGER,
        time_archived INTEGER,
        workspace_id TEXT,
        path TEXT,
        agent TEXT,
        model TEXT,
        FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE
      );

      CREATE TABLE message (
        id TEXT PRIMARY KEY,
        session_id TEXT NOT NULL,
        time_created INTEGER NOT NULL,
        time_updated INTEGER NOT NULL,
        data TEXT NOT NULL,
        FOREIGN KEY (session_id) REFERENCES session(id) ON DELETE CASCADE
      );

      CREATE TABLE part (
        id TEXT PRIMARY KEY,
        message_id TEXT NOT NULL,
        session_id TEXT NOT NULL,
        time_created INTEGER NOT NULL,
        time_updated INTEGER NOT NULL,
        data TEXT NOT NULL,
        FOREIGN KEY (message_id) REFERENCES message(id) ON DELETE CASCADE
      );

      CREATE INDEX part_session_idx ON part (session_id);
      CREATE INDEX session_project_idx ON session (project_id);
      CREATE INDEX message_session_time_created_id_idx ON message (session_id, time_created, id);
      CREATE INDEX part_message_id_id_idx ON part (message_id, id);
    `);
        db.prepare('INSERT INTO project (id, worktree, time_created, time_updated, sandboxes) VALUES (?, ?, ?, ?, ?)').run('project-1', workspacePath, 1_700_000_000_000, 1_700_000_001_000, '[]');
        db.prepare(`
      INSERT INTO session (
        id, project_id, slug, directory, title, version, time_created, time_updated, time_archived
      )
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run('open-session-1', 'project-1', 'open-session-1', workspacePath, 'OpenCode indexed title', '0.0.0', 1_700_000_000_000, 1_700_000_004_000, null);
        const userMessageData = JSON.stringify({
            role: 'user',
            time: { created: 1_700_000_001_000 },
            agent: 'test',
            model: { providerID: 'anthropic', modelID: 'claude' },
        });
        const assistantMessageData = JSON.stringify({
            role: 'assistant',
            time: { created: 1_700_000_002_000, completed: 1_700_000_003_000 },
            parentID: 'message-user',
            modelID: 'anthropic/claude-sonnet-4-5',
            providerID: 'anthropic',
            mode: 'default',
            agent: 'test',
            path: { cwd: '.', root: '.' },
            cost: 0.01,
            tokens: {
                input: 10,
                output: 20,
                reasoning: 0,
                cache: { read: 3, write: 2 },
            },
        });
        db.prepare('INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)').run('message-user', 'open-session-1', 1_700_000_001_000, 1_700_000_001_500, userMessageData);
        db.prepare('INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)').run('message-assistant', 'open-session-1', 1_700_000_002_000, 1_700_000_003_000, assistantMessageData);
        const insertPart = db.prepare(`
      INSERT INTO part (id, message_id, session_id, time_created, time_updated, data)
      VALUES (?, ?, ?, ?, ?, ?)
    `);
        insertPart.run('part-user-text', 'message-user', 'open-session-1', 1_700_000_001_000, 1_700_000_001_000, JSON.stringify({
            type: 'text',
            text: JSON.stringify('Build the OpenCode integration.'),
        }));
        insertPart.run('part-reasoning', 'message-assistant', 'open-session-1', 1_700_000_002_000, 1_700_000_002_000, JSON.stringify({
            type: 'reasoning',
            text: 'I will inspect the provider shape first.',
            time: { start: 0, end: 1 },
        }));
        insertPart.run('part-assistant-text', 'message-assistant', 'open-session-1', 1_700_000_002_500, 1_700_000_002_500, JSON.stringify({
            type: 'text',
            text: 'The provider is wired.',
        }));
        insertPart.run('part-tool', 'message-assistant', 'open-session-1', 1_700_000_003_000, 1_700_000_003_000, JSON.stringify({
            type: 'tool',
            tool: 'bash',
            callID: 'tool-call-1',
            state: {
                status: 'completed',
                input: { command: 'npm test' },
                output: 'ok',
                title: 'bash',
                metadata: {},
                time: { start: 0, end: 1 },
            },
        }));
    }
    finally {
        db.close();
    }
};
test('OpenCode session synchronizer indexes sqlite sessions without deletable transcript paths', { concurrency: false }, async () => {
    const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'opencode-session-sync-'));
    const workspacePath = path.join(tempRoot, 'workspace');
    await mkdir(workspacePath, { recursive: true });
    const restoreHomeDir = patchHomeDir(tempRoot);
    try {
        await createOpenCodeDatabase(tempRoot, workspacePath);
        await withIsolatedDatabase(() => {
            const synchronizer = new OpenCodeSessionSynchronizer();
            const processed = synchronizer.synchronize();
            return Promise.resolve(processed).then((count) => {
                assert.equal(count, 1);
                const indexed = sessionsDb.getSessionById('open-session-1');
                assert.equal(indexed?.provider, 'opencode');
                assert.equal(indexed?.project_path, workspacePath);
                assert.equal(indexed?.custom_name, 'OpenCode indexed title');
                assert.equal(indexed?.jsonl_path, null);
            });
        });
    }
    finally {
        restoreHomeDir();
        await rm(tempRoot, { recursive: true, force: true });
    }
});
test('OpenCode sessions provider normalizes quoted live text and skips user echoes', () => {
    const provider = new OpenCodeSessionsProvider();
    const normalized = provider.normalizeMessage({
        type: 'text',
        sessionID: 'open-session-live',
        text: JSON.stringify('hello bro'),
    }, null);
    assert.equal(normalized.length, 1);
    assert.equal(normalized[0]?.kind, 'stream_delta');
    assert.equal(normalized[0]?.content, 'hello bro');
    const userEcho = provider.normalizeMessage({
        type: 'text',
        sessionID: 'open-session-live',
        role: 'user',
        text: 'hello bro',
    }, null);
    assert.deepEqual(userEcho, []);
});
test('OpenCode sessions provider reads sqlite history and token usage', { concurrency: false }, async () => {
    const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'opencode-session-history-'));
    const workspacePath = path.join(tempRoot, 'workspace');
    await mkdir(workspacePath, { recursive: true });
    const restoreHomeDir = patchHomeDir(tempRoot);
    try {
        await createOpenCodeDatabase(tempRoot, workspacePath);
        const provider = new OpenCodeSessionsProvider();
        const history = await provider.fetchHistory('open-session-1');
        assert.equal(history.total, 4);
        assert.equal(history.messages[0]?.kind, 'text');
        assert.equal(history.messages[0]?.role, 'user');
        assert.equal(history.messages[0]?.content, 'Build the OpenCode integration.');
        assert.equal(history.messages[1]?.kind, 'thinking');
        assert.equal(history.messages[2]?.content, 'The provider is wired.');
        assert.equal(history.messages[3]?.kind, 'tool_use');
        assert.deepEqual(history.messages[3]?.toolResult, { content: 'ok', isError: false });
        assert.deepEqual(history.tokenUsage, {
            used: 35,
            total: 35,
            inputTokens: 10,
            outputTokens: 20,
            cacheReadTokens: 3,
            cacheCreationTokens: 2,
        });
        const paged = await provider.fetchHistory('open-session-1', { limit: 2, offset: 0 });
        assert.equal(paged.messages.length, 2);
        assert.equal(paged.hasMore, true);
        assert.equal(paged.messages[0]?.content, 'The provider is wired.');
    }
    finally {
        restoreHomeDir();
        await rm(tempRoot, { recursive: true, force: true });
    }
});
//# sourceMappingURL=opencode-sessions.test.js.map