use anyhow::Result;
use rusqlite::{params, Connection, OptionalExtension};
use std::path::PathBuf;
use std::sync::Mutex;
use tauri::{AppHandle, Manager};

pub struct DbState(pub Mutex<Connection>);

pub fn db_path(app: &AppHandle) -> PathBuf {
    app.path()
        .app_data_dir()
        .expect("Failed to get app data dir")
        .join("asw.db")
}

pub fn init(app: &AppHandle) -> Result<()> {
    let path = db_path(app);
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)?;
    }

    let conn = Connection::open(&path)?;
    // WAL mode: much faster for bulk writes; allows concurrent reads during writes
    conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;")?;
    conn.execute_batch(SCHEMA)?;

    app.manage(DbState(Mutex::new(conn)));
    Ok(())
}

pub fn get_config(conn: &Connection, key: &str) -> Result<Option<String>> {
    let mut stmt = conn.prepare("SELECT value FROM config WHERE key = ?1")?;
    let result = stmt.query_row(params![key], |row| row.get(0)).optional()?;
    Ok(result)
}

pub fn set_config(conn: &Connection, key: &str, value: &str) -> Result<()> {
    conn.execute(
        "INSERT OR REPLACE INTO config (key, value) VALUES (?1, ?2)",
        params![key, value],
    )?;
    Ok(())
}

const SCHEMA: &str = r#"
CREATE TABLE IF NOT EXISTS config (
    key   TEXT PRIMARY KEY,
    value TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS static_tiles (
    id          INTEGER PRIMARY KEY,
    name        TEXT NOT NULL,
    flags       INTEGER NOT NULL,
    weight      INTEGER NOT NULL,
    quality     INTEGER NOT NULL,
    height      INTEGER NOT NULL,
    hue         INTEGER NOT NULL,
    anim_id     INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS mobile_entries (
    body_id     INTEGER PRIMARY KEY,
    name        TEXT NOT NULL,
    flags       INTEGER NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_static_tiles_name ON static_tiles(name);

CREATE TABLE IF NOT EXISTS script_classes (
    name        TEXT PRIMARY KEY,
    namespace   TEXT NOT NULL,
    file_path   TEXT NOT NULL,
    base_class  TEXT,
    interfaces  TEXT NOT NULL DEFAULT '[]',
    attributes  TEXT NOT NULL DEFAULT '[]',
    is_gump     INTEGER NOT NULL DEFAULT 0,
    is_mobile   INTEGER NOT NULL DEFAULT 0,
    is_item     INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS script_methods (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    class_name      TEXT NOT NULL,
    name            TEXT NOT NULL,
    return_type     TEXT NOT NULL,
    parameters      TEXT NOT NULL DEFAULT '[]',
    is_override     INTEGER NOT NULL DEFAULT 0,
    is_virtual      INTEGER NOT NULL DEFAULT 0,
    calls_gump      INTEGER NOT NULL DEFAULT 0,
    gump_class      TEXT,
    FOREIGN KEY (class_name) REFERENCES script_classes(name) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_script_classes_namespace ON script_classes(namespace);
CREATE INDEX IF NOT EXISTS idx_script_classes_name ON script_classes(name);
CREATE INDEX IF NOT EXISTS idx_script_methods_class ON script_methods(class_name);
CREATE INDEX IF NOT EXISTS idx_script_methods_name ON script_methods(name);
"#;
