use std::sync::Arc;

use surrealdb::engine::remote::ws::Client;
use surrealdb::Surreal;
use tracing::debug;

use crate::error::{Result, WorkflowError};
use crate::instance::WorkflowInstance;

/// Persists `WorkflowInstance` records to SurrealDB for crash recovery.
///
/// Uses the injected connection from the backend — does NOT create its own DB
/// connection. Table: `workflow_instances` (created by migration
/// 009_workflow_state.surql).
pub struct SurrealWorkflowStore {
    db: Arc<Surreal<Client>>,
}

impl SurrealWorkflowStore {
    pub fn new(db: Arc<Surreal<Client>>) -> Self {
        Self { db }
    }

    /// Upsert a workflow instance. Called after every state-mutating operation.
    pub async fn save(&self, instance: &WorkflowInstance) -> Result<()> {
        let json = serde_json::to_value(instance).map_err(|e| {
            WorkflowError::DatabaseError(format!("serialize workflow {}: {e}", instance.id))
        })?;

        let _: Option<serde_json::Value> = self
            .db
            .upsert(("workflow_instances", &*instance.id))
            .content(json)
            .await
            .map_err(|e| {
                WorkflowError::DatabaseError(format!("save workflow {}: {e}", instance.id))
            })?;

        debug!(workflow_id = %instance.id, "Workflow instance persisted");
        Ok(())
    }

    /// Load a single instance by ID.
    pub async fn load(&self, id: &str) -> Result<Option<WorkflowInstance>> {
        let raw: Option<serde_json::Value> = self
            .db
            .select(("workflow_instances", id))
            .await
            .map_err(|e| WorkflowError::DatabaseError(format!("load workflow {id}: {e}")))?;

        raw.map(|v| {
            serde_json::from_value(v).map_err(|e| {
                WorkflowError::DatabaseError(format!("deserialize workflow {id}: {e}"))
            })
        })
        .transpose()
    }

    /// Load all non-terminal instances for crash recovery on startup.
    ///
    /// Fetches all rows and filters in Rust — avoids complex SurrealQL queries
    /// against serde-serialized enum variants (e.g. `Failed` serializes as an
    /// object).
    pub async fn load_active(&self) -> Result<Vec<WorkflowInstance>> {
        let mut response = self
            .db
            .query("SELECT * FROM workflow_instances")
            .await
            .map_err(|e| WorkflowError::DatabaseError(format!("load_active query: {e}")))?;

        let raw: Vec<serde_json::Value> = response
            .take(0)
            .map_err(|e| WorkflowError::DatabaseError(format!("load_active take: {e}")))?;

        let instances: Vec<WorkflowInstance> = raw
            .into_iter()
            .filter_map(|v| serde_json::from_value(v).ok())
            .collect();

        Ok(instances
            .into_iter()
            .filter(|i| !i.is_completed() && !i.is_failed() && !i.is_cancelled())
            .collect())
    }

    /// Delete a terminal workflow instance from the store.
    pub async fn delete(&self, id: &str) -> Result<()> {
        let _: Option<serde_json::Value> = self
            .db
            .delete(("workflow_instances", id))
            .await
            .map_err(|e| WorkflowError::DatabaseError(format!("delete workflow {id}: {e}")))?;

        debug!(workflow_id = %id, "Workflow instance deleted from store");
        Ok(())
    }
}
