import Database from 'better-sqlite3';
import fs from 'node:fs';
import path from 'node:path';
import type { Database as BetterSqlite3Database } from 'better-sqlite3';

/**
 * Local SQLite store for the kanban board.
 *
 * The database file lives at `<package>/.data/kanban.db` (gitignored). Using
 * SQLite (rather than a JSON file) gives us proper relational integrity for
 * comments/tags/references and concurrent-safe writes for free.
 */
const DATA_DIR = path.join(process.cwd(), '.data');
fs.mkdirSync(DATA_DIR, { recursive: true });
const DB_PATH = path.join(DATA_DIR, 'kanban.db');

export const db: BetterSqlite3Database = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

/** Row shapes returned by better-sqlite3 (snake_case columns). */
export interface CardRow {
  id: string;
  title: string;
  description: string;
  details: string;
  category: string;
  files: string | null;
  notes: string | null;
  status: string;
  sort_order: number;
  updated_at: string;
}

export interface ReferenceRow {
  label: string;
  type: string;
  href: string;
  canonical: number;
}

export interface CommentRow {
  id: string;
  card_id: string;
  text: string;
  author: string;
  created_at: number;
}

/** Create tables if missing. Idempotent. */
export function migrate(): void {
  // The agent_* tables are new and empty in dev; if an earlier schema created
  // them with non-nullable timestamp columns, recreate them so the nullable
  // columns below take effect without requiring a manual DB wipe.
  const cols = db.prepare('PRAGMA table_info(agent_runs)').all() as
    { name: string; notnull: number }[];
  const finishedCol = cols.find((c) => c.name === 'finished_at');
  if (finishedCol && finishedCol.notnull === 1) {
    db.exec('DROP TABLE IF EXISTS agent_run_events');
    db.exec('DROP TABLE IF EXISTS agent_runs');
  }

  db.exec(`
    CREATE TABLE IF NOT EXISTS cards (
      id         TEXT PRIMARY KEY,
      title      TEXT NOT NULL,
      description TEXT NOT NULL,
      details    TEXT NOT NULL,
      category   TEXT NOT NULL,
      files      TEXT,
      notes      TEXT,
      status     TEXT NOT NULL DEFAULT 'backlog',
      sort_order REAL NOT NULL DEFAULT 0,
      updated_at TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS card_references (
      card_id   TEXT NOT NULL,
      label     TEXT NOT NULL,
      type      TEXT NOT NULL,
      href      TEXT NOT NULL,
      canonical INTEGER NOT NULL DEFAULT 0,
      PRIMARY KEY (card_id, href)
    );

    CREATE TABLE IF NOT EXISTS comments (
      id         TEXT PRIMARY KEY,
      card_id    TEXT NOT NULL,
      text       TEXT NOT NULL,
      author     TEXT NOT NULL,
      created_at INTEGER NOT NULL
    );

    CREATE TABLE IF NOT EXISTS tags (
      card_id TEXT NOT NULL,
      tag     TEXT NOT NULL,
      PRIMARY KEY (card_id, tag)
    );

    CREATE TABLE IF NOT EXISTS custom_pages (
      id         TEXT PRIMARY KEY,
      slug       TEXT NOT NULL UNIQUE,
      title      TEXT NOT NULL,
      icon       TEXT NOT NULL DEFAULT '◈',
      blurb      TEXT NOT NULL DEFAULT '',
      content    TEXT NOT NULL DEFAULT '',
      html       TEXT NOT NULL DEFAULT '',
      beautified INTEGER NOT NULL DEFAULT 0,
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL
    );

    -- Agentic orchestrator runs. One run works a single kanban card inside an
    -- isolated git worktree, driving the board/docs via internal HTTP endpoints.
    CREATE TABLE IF NOT EXISTS agent_runs (
      id            TEXT PRIMARY KEY,
      card_id       TEXT NOT NULL,
      status        TEXT NOT NULL DEFAULT 'queued',
      use_worktree  INTEGER NOT NULL DEFAULT 1,
      branch        TEXT,
      worktree_path TEXT,
      prompt        TEXT NOT NULL DEFAULT '',
      summary       TEXT,
      token         TEXT NOT NULL,
      commit_sha    TEXT,
      error         TEXT,
      created_at    TEXT NOT NULL,
      started_at    TEXT,
      finished_at   TEXT
    );

    -- Slim, replayable event log streamed from each agent run.
    CREATE TABLE IF NOT EXISTS agent_run_events (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      run_id     TEXT NOT NULL,
      seq        INTEGER NOT NULL,
      type       TEXT NOT NULL,
      data       TEXT NOT NULL DEFAULT '{}',
      created_at TEXT NOT NULL,
      UNIQUE(run_id, seq)
    );

    CREATE INDEX IF NOT EXISTS idx_comments_card     ON comments(card_id, created_at);
    CREATE INDEX IF NOT EXISTS idx_tags_card          ON tags(card_id);
    CREATE INDEX IF NOT EXISTS idx_refs_card          ON card_references(card_id);
    CREATE INDEX IF NOT EXISTS idx_agent_runs_card    ON agent_runs(card_id);
    CREATE INDEX IF NOT EXISTS idx_agent_runs_status  ON agent_runs(status);
    CREATE INDEX IF NOT EXISTS idx_agent_events_run   ON agent_run_events(run_id, seq);
  `);

  // Additive columns for agent_runs: session persistence (so refinement runs
  // can resume a prior run's chat) and worktree ownership (refinement runs
  // inherit, never own, a worktree). Idempotent: only adds what's missing.
  const runCols = db.prepare('PRAGMA table_info(agent_runs)').all() as { name: string }[];
  const runColNames = new Set(runCols.map((c) => c.name));
  if (!runColNames.has('session_file')) {
    db.exec('ALTER TABLE agent_runs ADD COLUMN session_file TEXT');
  }
  if (!runColNames.has('owns_worktree')) {
    db.exec('ALTER TABLE agent_runs ADD COLUMN owns_worktree INTEGER NOT NULL DEFAULT 1');
  }
}

/** Row shape for the agent_runs table. */
export interface AgentRunRow {
  id: string;
  card_id: string;
  status: string;
  use_worktree: number;
  /** Whether this run created (and therefore owns/cleans up) its worktree.
   * Refinement runs inherit a prior run's worktree and set this to 0. */
  owns_worktree: number;
  branch: string | null;
  worktree_path: string | null;
  /** pi session JSONL path persisted for this run (resumable by refinements). */
  session_file: string | null;
  prompt: string;
  summary: string | null;
  token: string;
  commit_sha: string | null;
  error: string | null;
  created_at: string;
  started_at: string | null;
  finished_at: string | null;
}

/** Row shape for the agent_run_events table. */
export interface AgentRunEventRow {
  id: number;
  run_id: string;
  seq: number;
  type: string;
  data: string;
  created_at: string;
}

/** Row shape for the custom_pages table. */
export interface CustomPageRow {
  id: string;
  slug: string;
  title: string;
  icon: string;
  blurb: string;
  content: string;
  html: string;
  beautified: number;
  created_at: string;
  updated_at: string;
}

// Ensure the schema exists the moment this module is imported. Route modules
// prepare statements at top level, so tables must exist before they load.
migrate();
