//! Supabase-specific backend implementation.
use crate::client::backend::{
    BackendError, BackendResult, BackendType, DatabaseBackend, HealthStatus, PostgrestMethod,
    QueryLanguage, QueryResult, TranslatedQuery,
};
use crate::drivers::supabase::client::{HealthAwareSupabaseClient, SupabaseConnectionInfo};
use async_trait::async_trait;
use serde_json::Value;

/// ## `SupabaseBackend`
/// A Supabase backend powered by HealthAwareSupabaseClient.
/// It is used to execute queries on the Supabase database.
///
/// # Arguments
///
/// * `client` - The Supabase client.
///
/// # Returns
///
/// A `SupabaseBackend` instance.
///
pub struct SupabaseBackend {
    client: HealthAwareSupabaseClient,
}

impl SupabaseBackend {
    /// ## `new`
    /// Create a new Supabase backend.
    ///
    /// # Arguments
    ///
    /// * `info` - The Supabase connection info.
    ///
    /// # Returns
    ///
    /// A `SupabaseBackend` instance.
    ///
    pub fn new(info: SupabaseConnectionInfo) -> BackendResult<Self> {
        let client: HealthAwareSupabaseClient =
            HealthAwareSupabaseClient::new(info).map_err(|err| {
                BackendError::Generic(format!("failed to build supabase client: {err}"))
            })?;
        Ok(Self { client })
    }

    /// ## `from_env`
    /// Create a new Supabase backend from environment variables.
    ///
    /// # Arguments
    ///
    /// * `url_key` - The environment variable name for the Supabase URL.
    /// * `key_key` - The environment variable name for the Supabase key.
    ///
    /// # Returns
    ///
    /// A `BackendResult` containing the Supabase backend.
    ///
    pub fn from_env(url_key: &str, key_key: &str) -> BackendResult<Self> {
        let info: SupabaseConnectionInfo = SupabaseConnectionInfo::from_env(url_key, key_key)
            .map_err(|err| BackendError::Generic(err.to_string()))?;
        Self::new(info)
    }
}

/// ## `DatabaseBackend` implementation for SupabaseBackend.
#[async_trait]
impl DatabaseBackend for SupabaseBackend {
    /// ## `execute_query`
    /// Execute a query on the Supabase backend.
    ///
    /// # Arguments
    ///
    /// * `query` - The translated query to execute.
    ///
    /// # Returns
    ///
    /// A `BackendResult` containing the query result.
    ///
    async fn execute_query(&self, query: TranslatedQuery) -> BackendResult<QueryResult> {
        if !matches!(query.language, QueryLanguage::Postgrest) {
            return Err(BackendError::Generic(
                "Supabase backend only supports PostgREST-style queries".to_string(),
            ));
        }
        let method: PostgrestMethod = query.postgrest_method.unwrap_or(PostgrestMethod::Get);
        if !matches!(method, PostgrestMethod::Get) {
            return Err(BackendError::Generic(
                "Direct Supabase backend currently supports PostgREST GET only; use gateway-routed mode for condition-based mutations".to_string(),
            ));
        }

        let table: &str = query.table.as_deref().unwrap_or("");
        let rows: Vec<Value> = self
            .client
            .execute(table, &query.sql)
            .await
            .map_err(|err| BackendError::Generic(err.to_string()))?;

        let columns: Vec<String> = rows
            .first()
            .and_then(Value::as_object)
            .map(|object| object.keys().cloned().collect::<Vec<_>>())
            .unwrap_or_default();
        Ok(QueryResult::new(rows, columns, None, None, None))
    }
    /// ## `health_check`
    /// Check the health of the Supabase backend.
    ///
    /// # Arguments
    ///
    /// * `self` - The Supabase backend.
    ///
    /// # Returns
    ///
    /// A `BackendResult` containing the health status.
    ///
    async fn health_check(&self) -> BackendResult<HealthStatus> {
        if let Some(deadline) = self.client.is_offline() {
            Ok(HealthStatus::Offline(deadline))
        } else {
            Ok(HealthStatus::Healthy)
        }
    }
    /// ## `backend_type`
    /// Get the backend type.
    ///
    /// # Arguments
    ///
    /// * `self` - The Supabase backend.
    ///
    /// # Returns
    ///
    /// A `BackendType` containing the backend type.
    fn backend_type(&self) -> BackendType {
        BackendType::Supabase
    }

    /// ## `supports_sql`
    /// Check if the backend supports SQL.
    ///
    /// # Arguments
    ///
    /// * `self` - The Supabase backend.
    ///
    /// # Returns
    ///
    /// A `bool` indicating if the backend supports SQL.
    fn supports_sql(&self) -> bool {
        false
    }
    /// ## `supports_cql`
    /// Check if the backend supports CQL.
    ///
    /// # Arguments
    ///
    /// * `self` - The Supabase backend.
    ///
    /// # Returns
    ///
    /// A `bool` indicating if the backend supports CQL.
    fn supports_cql(&self) -> bool {
        false
    }

    /// ## `as_any`
    /// Get the backend as any.
    ///
    /// # Arguments
    ///
    /// * `self` - The Supabase backend.
    ///
    /// # Returns
    ///
    /// A `&dyn std::any::Any` containing the backend.
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}
