use anyhow::{Context, Result as AnyhowResult};
use bcrypt::{hash, verify, DEFAULT_COST}; // Add bcrypt dependency for password hashing
use rusqlite::{params, Connection, OptionalExtension};
use uuid::Uuid; // UUID for generating unique IDs // Import anyhow

pub fn init_db() -> AnyhowResult<Connection> {
    let conn = Connection::open("form_data.db").context("Failed to open the database")?;

    // Create tables
    conn.execute(
        "CREATE TABLE IF NOT EXISTS forms (
            id TEXT PRIMARY KEY,
            name TEXT NOT NULL,
            fields TEXT NOT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    )?;

    conn.execute(
        "CREATE TABLE IF NOT EXISTS submissions (
            id TEXT PRIMARY KEY,
            form_id TEXT NOT NULL,
            data TEXT NOT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (form_id) REFERENCES forms (id) ON DELETE CASCADE
        )",
        [],
    )?;

    // Add a table for users
    conn.execute(
        "CREATE TABLE IF NOT EXISTS users (
            id TEXT PRIMARY KEY,
            username TEXT NOT NULL UNIQUE,
            password TEXT NOT NULL, -- Store a hashed password
            token TEXT UNIQUE       -- Optional: For token-based auth
        )",
        [],
    )?;

    // Setup initial admin after creating the tables
    setup_initial_admin(&conn)?;

    Ok(conn)
}

pub fn setup_initial_admin(conn: &Connection) -> AnyhowResult<()> {
    add_admin_user(conn)?;
    Ok(())
}

pub fn add_admin_user(conn: &Connection) -> AnyhowResult<()> {
    // Check if admin user already exists
    let mut stmt = conn
        .prepare("SELECT id FROM users WHERE username = ?1")
        .context("Failed to prepare query for checking admin user")?;
    if stmt.exists(params!["admin"])? {
        return Ok(());
    }

    // Generate a UUID for the admin user
    let admin_id = Uuid::new_v4().to_string();

    // Hash the password before storing it
    let hashed_password = hash("admin", DEFAULT_COST).context("Failed to hash password")?;

    // Add admin user with hashed password
    conn.execute(
        "INSERT INTO users (id, username, password) VALUES (?1, ?2, ?3)",
        params![admin_id, "admin", hashed_password],
    )
    .context("Failed to insert admin user into the database")?;

    Ok(())
}

// Add a function to validate a token
pub fn validate_token(conn: &Connection, token: &str) -> AnyhowResult<Option<String>> {
    let mut stmt = conn
        .prepare("SELECT id FROM users WHERE token = ?1")
        .context("Failed to prepare query for validating token")?;
    let user_id: Option<String> = stmt
        .query_row(params![token], |row| row.get(0))
        .optional()
        .context("Failed to retrieve user ID for the given token")?;
    Ok(user_id)
}

// Add a function to authenticate users (for login)
pub fn authenticate_user(
    conn: &Connection,
    username: &str,
    password: &str,
) -> AnyhowResult<Option<String>> {
    let mut stmt = conn
        .prepare("SELECT id, password FROM users WHERE username = ?1")
        .context("Failed to prepare query for authenticating user")?;
    let mut rows = stmt
        .query(params![username])
        .context("Failed to execute query for authenticating user")?;

    if let Some(row) = rows.next()? {
        let user_id: String = row.get(0)?;
        let stored_password: String = row.get(1)?;

        // Use bcrypt to verify the hashed password
        if verify(password, &stored_password).context("Failed to verify password")? {
            return Ok(Some(user_id));
        }
    }
    Ok(None)
}

// Add a function to generate and save a token for a user
pub fn generate_token_for_user(conn: &Connection, user_id: &str, token: &str) -> AnyhowResult<()> {
    conn.execute(
        "UPDATE users SET token = ?1 WHERE id = ?2",
        params![token, user_id],
    )
    .context("Failed to update token for user")?;
    Ok(())
}