/**
 * SQLite-backed settings store for the Bun process.
 * Uses bun:sqlite (built-in, synchronous, zero external deps).
 * DB path: ~/.config/agor/settings.db
 */

import { Database } from "bun:sqlite";
import { homedir } from "os";
import { mkdirSync } from "fs";
import { join } from "path";

// ── DB path ──────────────────────────────────────────────────────────────────

const CONFIG_DIR = join(homedir(), ".config", "agor");
const DB_PATH    = join(CONFIG_DIR, "settings.db");

// ── Schema ───────────────────────────────────────────────────────────────────

const SCHEMA = `
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 500;

CREATE TABLE IF NOT EXISTS schema_version (
  version INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS settings (
  key   TEXT PRIMARY KEY,
  value TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS projects (
  id     TEXT PRIMARY KEY,
  config TEXT NOT NULL  -- JSON blob
);

CREATE TABLE IF NOT EXISTS custom_themes (
  id      TEXT PRIMARY KEY,
  name    TEXT NOT NULL,
  palette TEXT NOT NULL  -- JSON blob
);

CREATE TABLE IF NOT EXISTS groups (
  id       TEXT PRIMARY KEY,
  name     TEXT NOT NULL,
  icon     TEXT NOT NULL,
  position INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS keybindings (
  id    TEXT PRIMARY KEY,
  chord TEXT NOT NULL
);
`;

// Seed default groups (idempotent via INSERT OR IGNORE)
const SEED_GROUPS = `
INSERT OR IGNORE INTO groups VALUES ('dev',      'Development', '🔧', 0);
INSERT OR IGNORE INTO groups VALUES ('test',     'Testing',     '🧪', 1);
INSERT OR IGNORE INTO groups VALUES ('ops',      'DevOps',      '🚀', 2);
INSERT OR IGNORE INTO groups VALUES ('research', 'Research',    '🔬', 3);
`;

// ── SettingsDb class ─────────────────────────────────────────────────────────

export interface ProjectConfig {
  id: string;
  name: string;
  cwd: string;
  accent?: string;
  provider?: string;
  profile?: string;
  model?: string;
  /** Group this project belongs to. Defaults to 'dev'. */
  groupId?: string;
  /** For clones: path of the source repo (worktree parent). */
  mainRepoPath?: string;
  /** For clones: ID of the original project this was cloned from. */
  cloneOf?: string;
  /** For clones: absolute path to the git worktree. */
  worktreePath?: string;
  /** For clones: branch name checked out in the worktree. */
  worktreeBranch?: string;
  /** 1-indexed clone number within the parent (1–3). */
  cloneIndex?: number;
  [key: string]: unknown;
}

export interface CustomTheme {
  id: string;
  name: string;
  palette: Record<string, string>;
}

export interface Group {
  id: string;
  name: string;
  icon: string;
  position: number;
}

export class SettingsDb {
  private db: Database;

  constructor() {
    // Ensure config dir exists before opening DB
    mkdirSync(CONFIG_DIR, { recursive: true });

    this.db = new Database(DB_PATH);
    this.db.exec(SCHEMA);
    this.db.exec(SEED_GROUPS);

    // Run version-tracked migrations
    this.runMigrations();
  }

  /** Run version-tracked schema migrations. */
  private runMigrations(): void {
    const CURRENT_VERSION = 1;

    const row = this.db
      .query<{ version: number }, []>("SELECT version FROM schema_version LIMIT 1")
      .get();
    const currentVersion = row?.version ?? 0;

    if (currentVersion < 1) {
      // Version 1 is the initial schema — already created above via SCHEMA.
      // Future migrations go here as version checks:
      //   if (currentVersion < 2) { this.db.exec("ALTER TABLE ..."); }
      //   if (currentVersion < 3) { this.db.exec("ALTER TABLE ..."); }
    }

    if (!row) {
      this.db.exec(`INSERT INTO schema_version (version) VALUES (${CURRENT_VERSION})`);
    } else if (currentVersion < CURRENT_VERSION) {
      this.db.exec(`UPDATE schema_version SET version = ${CURRENT_VERSION}`);
    }
  }

  // ── Settings ──────────────────────────────────────────────────────────────

  getSetting(key: string): string | null {
    const row = this.db
      .query<{ value: string }, [string]>("SELECT value FROM settings WHERE key = ?")
      .get(key);
    return row?.value ?? null;
  }

  setSetting(key: string, value: string): void {
    this.db
      .query("INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value")
      .run(key, value);
  }

  getAll(): Record<string, string> {
    const rows = this.db
      .query<{ key: string; value: string }, []>("SELECT key, value FROM settings")
      .all();
    return Object.fromEntries(rows.map((r) => [r.key, r.value]));
  }

  // ── Projects ──────────────────────────────────────────────────────────────

  getProject(id: string): ProjectConfig | null {
    const row = this.db
      .query<{ config: string }, [string]>("SELECT config FROM projects WHERE id = ?")
      .get(id);
    if (!row) return null;
    try {
      return JSON.parse(row.config) as ProjectConfig;
    } catch {
      console.error(`[settings-db] Failed to parse project config for id=${id}`);
      return null;
    }
  }

  setProject(id: string, config: ProjectConfig): void {
    const json = JSON.stringify(config);
    this.db
      .query("INSERT INTO projects (id, config) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET config = excluded.config")
      .run(id, json);
  }

  listProjects(): ProjectConfig[] {
    const rows = this.db
      .query<{ config: string }, []>("SELECT config FROM projects")
      .all();
    return rows.flatMap((r) => {
      try {
        return [JSON.parse(r.config) as ProjectConfig];
      } catch {
        return [];
      }
    });
  }

  // ── Groups ─────────────────────────────────────────────────────────────────

  listGroups(): Group[] {
    return this.db
      .query<Group, []>("SELECT id, name, icon, position FROM groups ORDER BY position ASC")
      .all();
  }

  // ── Custom Themes ─────────────────────────────────────────────────────────

  getCustomThemes(): CustomTheme[] {
    const rows = this.db
      .query<{ id: string; name: string; palette: string }, []>(
        "SELECT id, name, palette FROM custom_themes"
      )
      .all();
    return rows.flatMap((r) => {
      try {
        return [{ id: r.id, name: r.name, palette: JSON.parse(r.palette) }];
      } catch {
        return [];
      }
    });
  }

  saveCustomTheme(id: string, name: string, palette: Record<string, string>): void {
    const json = JSON.stringify(palette);
    this.db
      .query(
        "INSERT INTO custom_themes (id, name, palette) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, palette = excluded.palette"
      )
      .run(id, name, json);
  }

  deleteCustomTheme(id: string): void {
    this.db.query("DELETE FROM custom_themes WHERE id = ?").run(id);
  }

  // ── Keybindings ───────────────────────────────────────────────────────────

  getKeybindings(): Record<string, string> {
    const rows = this.db
      .query<{ id: string; chord: string }, []>("SELECT id, chord FROM keybindings")
      .all();
    return Object.fromEntries(rows.map((r) => [r.id, r.chord]));
  }

  setKeybinding(id: string, chord: string): void {
    this.db
      .query("INSERT INTO keybindings (id, chord) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET chord = excluded.chord")
      .run(id, chord);
  }

  deleteKeybinding(id: string): void {
    this.db.query("DELETE FROM keybindings WHERE id = ?").run(id);
  }

  // ── Lifecycle ─────────────────────────────────────────────────────────────

  close(): void {
    this.db.close();
  }
}

// Singleton — one DB handle for the process lifetime
export const settingsDb = new SettingsDb();
