use std::sync::Arc;

use surrealdb::{engine::any::Any, Surreal};
use tracing::info;

use crate::{config::DbConfig, migrate::migrate, Error};

/// Shared SurrealDB connection pool.
///
/// Internally `Arc`-backed — cheap to clone, safe to share across tasks.
#[derive(Clone, Debug)]
pub struct SurrealPool {
    inner: Arc<Surreal<Any>>,
}

impl SurrealPool {
    /// Connect to SurrealDB according to `config` and run schema migrations.
    pub async fn connect(config: &DbConfig) -> Result<Self, Error> {
        let db: Surreal<Any> = match config {
            DbConfig::Memory => {
                info!("SurrealDB: in-process memory backend");
                surrealdb::engine::any::connect("mem://")
                    .await
                    .map_err(|e| Error::Connect(e.to_string()))?
            }

            #[cfg(feature = "embedded-surrealkv")]
            DbConfig::EmbeddedSurrealKV { path } => {
                info!(path, "SurrealDB: embedded SurrealKV backend (core/graph)");
                surrealdb::engine::any::connect(format!("surrealkv://{path}"))
                    .await
                    .map_err(|e| Error::Connect(e.to_string()))?
            }

            #[cfg(feature = "embedded-rocksdb")]
            DbConfig::EmbeddedRocksDB { path } => {
                info!(path, "SurrealDB: embedded RocksDB backend (hot data)");
                surrealdb::engine::any::connect(format!("rocksdb://{path}"))
                    .await
                    .map_err(|e| Error::Connect(e.to_string()))?
            }

            DbConfig::Server { url, .. } => {
                info!(url, "SurrealDB: WebSocket server backend");
                surrealdb::engine::any::connect(url.as_str())
                    .await
                    .map_err(|e| Error::Connect(e.to_string()))?
            }

            // Fallback for embedded variants when the feature is disabled.
            #[cfg(not(feature = "embedded-surrealkv"))]
            DbConfig::EmbeddedSurrealKV { .. } => {
                return Err(Error::Connect(
                    "SurrealKV requires the 'embedded-surrealkv' feature".into(),
                ));
            }
            #[cfg(not(feature = "embedded-rocksdb"))]
            DbConfig::EmbeddedRocksDB { .. } => {
                return Err(Error::Connect(
                    "RocksDB requires the 'embedded-rocksdb' feature".into(),
                ));
            }
        };

        // Only remote backends require signin — embedded engines have no auth subsystem.
        if let DbConfig::Server { .. } = config {
            db.signin(surrealdb::opt::auth::Root {
                username: "root".to_string(),
                password: "root".to_string(),
            })
            .await
            .map_err(|e| Error::Connect(e.to_string()))?;
        }

        migrate(&db).await?;

        Ok(Self {
            inner: Arc::new(db),
        })
    }

    /// Obtain a reference to the underlying `Surreal<Any>` client.
    pub fn db(&self) -> &Surreal<Any> {
        &self.inner
    }

    /// Verify connectivity.
    pub async fn health(&self) -> Result<(), Error> {
        self.inner
            .query("RETURN true")
            .await
            .map_err(|e| Error::Health(e.to_string()))?;
        Ok(())
    }
}
