use rusqlite::{Connection, Result, params};
use serde::{Deserialize, Serialize};
use chrono::Utc;
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Profile {
    pub id: String,
    pub name: String,
    pub created_at: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GuideRow {
    pub gid: String,
    pub name: String,
    pub data: String,
    pub last_synced_at: Option<String>,
}

pub fn get_db_path() -> String {
    let data_dir = dirs_next::data_dir()
        .unwrap_or_else(|| std::path::PathBuf::from("."))
        .join("toughligui");
    std::fs::create_dir_all(&data_dir).ok();
    data_dir.join("toughligui.db").to_string_lossy().to_string()
}

pub fn open() -> Result<Connection> {
    let path = get_db_path();
    let conn = Connection::open(&path)?;
    conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?;
    Ok(conn)
}

pub fn migrate(conn: &Connection) -> Result<()> {
    conn.execute_batch("
        CREATE TABLE IF NOT EXISTS profiles (
            id TEXT PRIMARY KEY,
            name TEXT UNIQUE NOT NULL,
            created_at TEXT NOT NULL
        );

        CREATE TABLE IF NOT EXISTS guides (
            gid TEXT PRIMARY KEY,
            name TEXT NOT NULL,
            data TEXT NOT NULL,
            last_synced_at TEXT
        );

        CREATE TABLE IF NOT EXISTS quest_completions (
            profile_id TEXT NOT NULL,
            quest_name TEXT NOT NULL,
            completed_at TEXT NOT NULL,
            PRIMARY KEY (profile_id, quest_name),
            FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
        );

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

pub fn get_profiles(conn: &Connection) -> Result<Vec<Profile>> {
    let mut stmt = conn.prepare("SELECT id, name, created_at FROM profiles ORDER BY created_at ASC")?;
    let rows = stmt.query_map([], |row| {
        Ok(Profile {
            id: row.get(0)?,
            name: row.get(1)?,
            created_at: row.get(2)?,
        })
    })?;
    rows.collect()
}

pub fn create_profile(conn: &Connection, name: &str) -> Result<Profile> {
    let id = Uuid::new_v4().to_string();
    let created_at = Utc::now().to_rfc3339();
    conn.execute(
        "INSERT INTO profiles (id, name, created_at) VALUES (?1, ?2, ?3)",
        params![id, name, created_at],
    )?;
    Ok(Profile { id, name: name.to_string(), created_at })
}

pub fn delete_profile(conn: &Connection, profile_id: &str) -> Result<()> {
    conn.execute("DELETE FROM profiles WHERE id = ?1", params![profile_id])?;
    Ok(())
}

pub fn get_completed_quests(conn: &Connection, profile_id: &str) -> Result<Vec<String>> {
    let mut stmt = conn.prepare(
        "SELECT quest_name FROM quest_completions WHERE profile_id = ?1"
    )?;
    let rows = stmt.query_map(params![profile_id], |row| row.get(0))?;
    rows.collect()
}

pub fn toggle_quest(conn: &Connection, profile_id: &str, quest_name: &str) -> Result<bool> {
    let exists: bool = conn.query_row(
        "SELECT COUNT(*) FROM quest_completions WHERE profile_id = ?1 AND quest_name = ?2",
        params![profile_id, quest_name],
        |row| row.get::<_, i64>(0),
    ).map(|c| c > 0)?;

    if exists {
        conn.execute(
            "DELETE FROM quest_completions WHERE profile_id = ?1 AND quest_name = ?2",
            params![profile_id, quest_name],
        )?;
        Ok(false)
    } else {
        let now = Utc::now().to_rfc3339();
        conn.execute(
            "INSERT INTO quest_completions (profile_id, quest_name, completed_at) VALUES (?1, ?2, ?3)",
            params![profile_id, quest_name, now],
        )?;
        Ok(true)
    }
}

pub fn upsert_guide(conn: &Connection, gid: &str, name: &str, data: &str) -> Result<()> {
    let now = Utc::now().to_rfc3339();
    conn.execute(
        "INSERT INTO guides (gid, name, data, last_synced_at) VALUES (?1, ?2, ?3, ?4)
         ON CONFLICT(gid) DO UPDATE SET name=excluded.name, data=excluded.data, last_synced_at=excluded.last_synced_at",
        params![gid, name, data, now],
    )?;
    Ok(())
}

pub fn get_guides(conn: &Connection) -> Result<Vec<GuideRow>> {
    let mut stmt = conn.prepare("SELECT gid, name, data, last_synced_at FROM guides ORDER BY rowid ASC")?;
    let rows = stmt.query_map([], |row| {
        Ok(GuideRow {
            gid: row.get(0)?,
            name: row.get(1)?,
            data: row.get(2)?,
            last_synced_at: row.get(3)?,
        })
    })?;
    rows.collect()
}

pub fn get_setting(conn: &Connection, key: &str) -> Option<String> {
    conn.query_row(
        "SELECT value FROM settings WHERE key = ?1",
        params![key],
        |row| row.get(0),
    ).ok()
}

pub fn set_setting(conn: &Connection, key: &str, value: &str) -> Result<()> {
    conn.execute(
        "INSERT INTO settings (key, value) VALUES (?1, ?2)
         ON CONFLICT(key) DO UPDATE SET value=excluded.value",
        params![key, value],
    )?;
    Ok(())
}
