import Database from 'better-sqlite3';
import { existsSync, mkdirSync } from 'node:fs';
import { dirname } from 'node:path';

let db: Database.Database | null = null;

export function getDb(dbPath: string): Database.Database {
  if (db) return db;

  const dir = dirname(dbPath);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }

  db = new Database(dbPath);
  db.pragma('journal_mode = WAL');
  db.pragma('foreign_keys = ON');
  initSchema(db);
  return db;
}

function initSchema(db: Database.Database) {
  // Migrations for existing tables (catch duplicate column errors silently)
  const migrate = (sql: string) => {
    try { db.exec(sql); } catch (e: any) {
      if (!String(e.message).includes('duplicate column')) console.error('[db] Migration failed:', sql, e.message);
    }
  };
  migrate('ALTER TABLE tasks ADD COLUMN scheduled_at TEXT');
  migrate("ALTER TABLE tasks ADD COLUMN mode TEXT NOT NULL DEFAULT 'prompt'");
  migrate('ALTER TABLE tasks ADD COLUMN watch_config TEXT');
  migrate("ALTER TABLE skills ADD COLUMN type TEXT NOT NULL DEFAULT 'skill'");
  migrate('ALTER TABLE skills ADD COLUMN archive TEXT');
  migrate("ALTER TABLE skills ADD COLUMN installed_version TEXT NOT NULL DEFAULT ''");
  migrate('ALTER TABLE skills ADD COLUMN rating REAL DEFAULT 0');
  migrate('ALTER TABLE skills ADD COLUMN deleted_remotely INTEGER NOT NULL DEFAULT 0');
  // 'registry' (synced from forge-skills) vs 'local' (uploaded by user).
  // Local skills are kept across syncs (they're not in the remote registry,
  // so the deleted_remotely housekeeping would otherwise wipe them).
  migrate("ALTER TABLE skills ADD COLUMN source TEXT NOT NULL DEFAULT 'registry'");
  migrate('ALTER TABLE project_pipelines ADD COLUMN last_run_at TEXT');
  migrate('ALTER TABLE pipeline_runs ADD COLUMN dedup_key TEXT');
  migrate("ALTER TABLE tasks ADD COLUMN agent TEXT DEFAULT 'claude'");
  // Recreate token_usage with day column (drop old version if schema changed)
  try { db.exec("SELECT day FROM token_usage LIMIT 1"); } catch { try { db.exec("DROP TABLE IF EXISTS token_usage"); db.exec("DROP TABLE IF EXISTS usage_scan_state"); } catch {} }
  // Unique index for dedup (only applies when dedup_key is NOT NULL)
  try { db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_pipeline_runs_dedup ON pipeline_runs(project_path, workflow_name, dedup_key)'); } catch {}
  // One-shot migration of old issue_autofix_processed → pipeline_runs.
  // Previously this ran every startup (reading + re-inserting all rows on
  // every cold worker boot — visible as `[db] Migrated N records …`).
  // Now: skip entirely if the source table has zero rows OR if any rows
  // have already been migrated (presence of issue: dedup keys is the
  // tell — INSERT OR IGNORE already protects against dups, the loop
  // itself was just wasted work).
  try {
    const alreadyMigrated = db.prepare("SELECT 1 FROM pipeline_runs WHERE dedup_key LIKE 'issue:%' LIMIT 1").get();
    if (!alreadyMigrated) {
      const old = db.prepare('SELECT * FROM issue_autofix_processed').all() as any[];
      if (old.length > 0) {
        const ins = db.prepare('INSERT OR IGNORE INTO pipeline_runs (id, project_path, workflow_name, pipeline_id, status, dedup_key, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)');
        for (const r of old) {
          ins.run(
            r.pipeline_id?.slice(0, 8) || ('mig-' + r.issue_number),
            r.project_path, 'issue-fix-and-review', r.pipeline_id || '',
            r.status === 'processing' ? 'running' : (r.status || 'done'),
            `issue:${r.issue_number}`, r.created_at || new Date().toISOString()
          );
        }
        console.log(`[db] Migrated ${old.length} issue_autofix_processed records to pipeline_runs (one-shot)`);
      }
    }
  } catch {}

  db.exec(`
    CREATE TABLE IF NOT EXISTS sessions (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      template_id TEXT NOT NULL,
      provider TEXT NOT NULL,
      model TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'idle',
      memory_config TEXT NOT NULL,
      system_prompt TEXT NOT NULL DEFAULT '',
      created_at TEXT NOT NULL DEFAULT (datetime('now')),
      updated_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS messages (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
      role TEXT NOT NULL,
      content TEXT NOT NULL,
      provider TEXT NOT NULL,
      model TEXT NOT NULL,
      token_count INTEGER DEFAULT 0,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, created_at);

    CREATE TABLE IF NOT EXISTS usage (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      provider TEXT NOT NULL,
      model TEXT NOT NULL,
      session_id TEXT REFERENCES sessions(id),
      input_tokens INTEGER NOT NULL DEFAULT 0,
      output_tokens INTEGER NOT NULL DEFAULT 0,
      cost REAL NOT NULL DEFAULT 0,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_usage_provider ON usage(provider, created_at);

    CREATE TABLE IF NOT EXISTS tasks (
      id TEXT PRIMARY KEY,
      project_name TEXT NOT NULL,
      project_path TEXT NOT NULL,
      prompt TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'queued',
      priority INTEGER NOT NULL DEFAULT 0,
      conversation_id TEXT,
      log TEXT NOT NULL DEFAULT '[]',
      result_summary TEXT,
      git_diff TEXT,
      git_branch TEXT,
      cost_usd REAL,
      error TEXT,
      created_at TEXT NOT NULL DEFAULT (datetime('now')),
      started_at TEXT,
      completed_at TEXT,
      scheduled_at TEXT,
      mode TEXT NOT NULL DEFAULT 'prompt',
      watch_config TEXT
    );

    CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status, created_at);

    -- Cached Claude CLI sessions for tree view
    CREATE TABLE IF NOT EXISTS cached_sessions (
      project_name TEXT NOT NULL,
      session_id TEXT NOT NULL,
      summary TEXT,
      first_prompt TEXT,
      message_count INTEGER DEFAULT 0,
      entry_count INTEGER DEFAULT 0,
      created TEXT,
      modified TEXT,
      git_branch TEXT,
      file_size INTEGER DEFAULT 0,
      last_synced TEXT NOT NULL DEFAULT (datetime('now')),
      PRIMARY KEY (project_name, session_id)
    );

    CREATE INDEX IF NOT EXISTS idx_cached_sessions_project ON cached_sessions(project_name, modified);

    -- In-app notifications
    CREATE TABLE IF NOT EXISTS notifications (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      type TEXT NOT NULL,
      title TEXT NOT NULL,
      body TEXT,
      read INTEGER NOT NULL DEFAULT 0,
      task_id TEXT,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_notifications_read ON notifications(read, created_at);

    -- Skills registry cache
    CREATE TABLE IF NOT EXISTS skills (
      name TEXT PRIMARY KEY,
      type TEXT NOT NULL DEFAULT 'skill',
      display_name TEXT NOT NULL,
      description TEXT,
      author TEXT,
      version TEXT,
      tags TEXT,
      score INTEGER DEFAULT 0,
      rating REAL DEFAULT 0,
      source_url TEXT,
      archive TEXT,
      installed_global INTEGER NOT NULL DEFAULT 0,
      installed_projects TEXT NOT NULL DEFAULT '[]',
      installed_version TEXT NOT NULL DEFAULT '',
      synced_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    -- Tab state (projects, docs, etc)
    CREATE TABLE IF NOT EXISTS tab_state (
      type TEXT PRIMARY KEY,
      data TEXT NOT NULL DEFAULT '{}'
    );

    -- Project pipeline bindings (which workflows are attached to which projects)
    CREATE TABLE IF NOT EXISTS project_pipelines (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      project_path TEXT NOT NULL,
      project_name TEXT NOT NULL,
      workflow_name TEXT NOT NULL,
      enabled INTEGER NOT NULL DEFAULT 1,
      config TEXT NOT NULL DEFAULT '{}',
      created_at TEXT NOT NULL DEFAULT (datetime('now')),
      UNIQUE(project_path, workflow_name)
    );

    -- Pipeline execution log (per project, replaces issue_autofix_processed)
    CREATE TABLE IF NOT EXISTS pipeline_runs (
      id TEXT PRIMARY KEY,
      project_path TEXT NOT NULL,
      workflow_name TEXT NOT NULL,
      pipeline_id TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'running',
      summary TEXT,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    -- Project favorites
    CREATE TABLE IF NOT EXISTS project_favorites (
      project_path TEXT PRIMARY KEY,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    -- Session watchers — monitor sessions and notify via Telegram
    CREATE TABLE IF NOT EXISTS session_watchers (
      id TEXT PRIMARY KEY,
      project_name TEXT NOT NULL,
      session_id TEXT,
      label TEXT,
      check_interval INTEGER NOT NULL DEFAULT 60,
      last_entry_count INTEGER DEFAULT 0,
      last_checked TEXT,
      notify_on_change INTEGER NOT NULL DEFAULT 1,
      notify_on_idle INTEGER NOT NULL DEFAULT 1,
      idle_threshold INTEGER NOT NULL DEFAULT 300,
      active INTEGER NOT NULL DEFAULT 1,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    );

    -- Token usage tracking (per session + day + model for accurate daily breakdown)
    CREATE TABLE IF NOT EXISTS token_usage (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      session_id TEXT NOT NULL,
      source TEXT NOT NULL DEFAULT 'terminal',
      project_path TEXT NOT NULL,
      project_name TEXT NOT NULL,
      model TEXT NOT NULL DEFAULT 'unknown',
      day TEXT NOT NULL,
      input_tokens INTEGER NOT NULL DEFAULT 0,
      output_tokens INTEGER NOT NULL DEFAULT 0,
      cache_read_tokens INTEGER NOT NULL DEFAULT 0,
      cache_create_tokens INTEGER NOT NULL DEFAULT 0,
      cost_usd REAL NOT NULL DEFAULT 0,
      message_count INTEGER NOT NULL DEFAULT 0,
      task_id TEXT,
      UNIQUE(session_id, source, model, day)
    );

    -- Track scan progress for incremental JSONL scanning
    CREATE TABLE IF NOT EXISTS usage_scan_state (
      file_path TEXT PRIMARY KEY,
      last_size INTEGER NOT NULL DEFAULT 0,
      last_scan TEXT NOT NULL DEFAULT (datetime('now'))
    );
  `);
}

export function closeDb() {
  if (db) {
    db.close();
    db = null;
  }
}
