use rusqlite::Connection;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use crate::error::{AppError, AppResult};

pub struct DatabaseManager {
    connections: HashMap<String, Connection>,
    base_path: PathBuf,
}

impl DatabaseManager {
    pub fn new(base_path: PathBuf) -> Self {
        Self {
            connections: HashMap::new(),
            base_path,
        }
    }

    pub fn get_db_path(&self, user_id: &str) -> PathBuf {
        self.base_path.join(format!("eritors-local-{}.db", user_id))
    }

    pub fn initialize(&mut self, user_id: &str) -> AppResult<()> {
        if self.connections.contains_key(user_id) {
            return Ok(());
        }

        let db_path = self.get_db_path(user_id);

        if let Some(parent) = db_path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        let conn = Connection::open(&db_path)?;

        conn.execute_batch("PRAGMA journal_mode=WAL;")?;
        conn.execute_batch("PRAGMA foreign_keys=ON;")?;

        self.connections.insert(user_id.to_string(), conn);
        Ok(())
    }

    pub fn get_connection(&self, user_id: &str) -> AppResult<&Connection> {
        self.connections
            .get(user_id)
            .ok_or_else(|| AppError::Database(rusqlite::Error::InvalidParameterName(
                format!("No database connection for user: {}", user_id),
            )))
    }

    pub fn close(&mut self, user_id: &str) {
        self.connections.remove(user_id);
    }

    pub fn close_all(&mut self) {
        self.connections.clear();
    }

    pub fn base_path(&self) -> &PathBuf {
        &self.base_path
    }
}

pub type DbManager = Arc<Mutex<DatabaseManager>>;

pub fn create_db_manager(base_path: PathBuf) -> DbManager {
    Arc::new(Mutex::new(DatabaseManager::new(base_path)))
}
