use chrono::Utc;
use surrealdb::engine::remote::ws::Client;
use surrealdb::Surreal;
use vapora_shared::models::{Agent, AgentRole, AgentStatus};
use vapora_shared::{Result, VaporaError};

/// Service for managing agents
#[derive(Clone)]
pub struct AgentService {
    db: Surreal<Client>,
}

impl AgentService {
    /// Create a new AgentService instance
    pub fn new(db: Surreal<Client>) -> Self {
        Self { db }
    }

    /// Register a new agent
    pub async fn register_agent(&self, mut agent: Agent) -> Result<Agent> {
        agent.created_at = Utc::now();

        let existing = self.get_agent_by_role(&agent.role).await;
        if existing.is_ok() {
            return Err(VaporaError::InvalidInput(format!(
                "Agent with role '{:?}' already exists",
                agent.role
            )));
        }

        let json = serde_json::to_value(&agent)?;
        let raw: Option<serde_json::Value> = self.db.create("agents").content(json).await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to register agent".to_string()))
    }

    /// List all agents
    pub async fn list_agents(&self) -> Result<Vec<Agent>> {
        let mut response = self
            .db
            .query("SELECT * FROM agents ORDER BY role ASC")
            .await?;

        let raw: Vec<serde_json::Value> = response.take(0)?;
        Ok(raw
            .into_iter()
            .filter_map(|v| serde_json::from_value(v).ok())
            .collect())
    }

    /// List agents by status
    #[allow(dead_code)]
    pub async fn list_agents_by_status(&self, status: AgentStatus) -> Result<Vec<Agent>> {
        let status_str = match status {
            AgentStatus::Active => "active",
            AgentStatus::Inactive => "inactive",
            AgentStatus::Updating => "updating",
            AgentStatus::Error => "error",
        };

        let mut response = self
            .db
            .query("SELECT * FROM agents WHERE status = $status ORDER BY role ASC")
            .bind(("status", status_str.to_string()))
            .await?;

        let raw: Vec<serde_json::Value> = response.take(0)?;
        Ok(raw
            .into_iter()
            .filter_map(|v| serde_json::from_value(v).ok())
            .collect())
    }

    /// Get an agent by ID
    pub async fn get_agent(&self, id: &str) -> Result<Agent> {
        let raw: Option<serde_json::Value> = self.db.select(("agents", id)).await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::NotFound(format!("Agent with id '{}' not found", id)))
    }

    /// Get an agent by role
    pub async fn get_agent_by_role(&self, role: &AgentRole) -> Result<Agent> {
        let role_str = match role {
            AgentRole::Architect => "architect",
            AgentRole::Developer => "developer",
            AgentRole::CodeReviewer => "code_reviewer",
            AgentRole::Tester => "tester",
            AgentRole::Documenter => "documenter",
            AgentRole::Marketer => "marketer",
            AgentRole::Presenter => "presenter",
            AgentRole::DevOps => "dev_ops",
            AgentRole::Monitor => "monitor",
            AgentRole::Security => "security",
            AgentRole::ProjectManager => "project_manager",
            AgentRole::DecisionMaker => "decision_maker",
        };

        let mut response = self
            .db
            .query("SELECT * FROM agents WHERE role = $role LIMIT 1")
            .bind(("role", role_str.to_string()))
            .await?;

        let raw: Vec<serde_json::Value> = response.take(0)?;
        raw.into_iter()
            .find_map(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::NotFound(format!("Agent with role '{:?}' not found", role)))
    }

    /// Update an agent
    pub async fn update_agent(&self, id: &str, mut updates: Agent) -> Result<Agent> {
        let existing = self.get_agent(id).await?;
        updates.id = existing.id;
        updates.created_at = existing.created_at;

        let json = serde_json::to_value(&updates)?;
        let raw: Option<serde_json::Value> = self.db.update(("agents", id)).content(json).await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to update agent".to_string()))
    }

    /// Update agent status
    pub async fn update_agent_status(&self, id: &str, status: AgentStatus) -> Result<Agent> {
        self.get_agent(id).await?;

        let raw: Option<serde_json::Value> = self
            .db
            .update(("agents", id))
            .merge(serde_json::json!({ "status": status }))
            .await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to update agent status".to_string()))
    }

    /// Add capability to an agent
    pub async fn add_capability(&self, id: &str, capability: String) -> Result<Agent> {
        let mut agent = self.get_agent(id).await?;

        if !agent.capabilities.contains(&capability) {
            agent.capabilities.push(capability);

            let raw: Option<serde_json::Value> = self
                .db
                .update(("agents", id))
                .merge(serde_json::json!({ "capabilities": agent.capabilities }))
                .await?;
            return raw
                .and_then(|v| serde_json::from_value(v).ok())
                .ok_or_else(|| VaporaError::DatabaseError("Failed to add capability".to_string()));
        }

        Ok(agent)
    }

    /// Remove capability from an agent
    pub async fn remove_capability(&self, id: &str, capability: &str) -> Result<Agent> {
        let mut agent = self.get_agent(id).await?;
        agent.capabilities.retain(|c| c != capability);

        let raw: Option<serde_json::Value> = self
            .db
            .update(("agents", id))
            .merge(serde_json::json!({ "capabilities": agent.capabilities }))
            .await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to remove capability".to_string()))
    }

    /// Add skill to an agent
    pub async fn add_skill(&self, id: &str, skill: String) -> Result<Agent> {
        let mut agent = self.get_agent(id).await?;

        if !agent.skills.contains(&skill) {
            agent.skills.push(skill);

            let raw: Option<serde_json::Value> = self
                .db
                .update(("agents", id))
                .merge(serde_json::json!({ "skills": agent.skills }))
                .await?;
            return raw
                .and_then(|v| serde_json::from_value(v).ok())
                .ok_or_else(|| VaporaError::DatabaseError("Failed to add skill".to_string()));
        }

        Ok(agent)
    }

    /// Deregister an agent
    pub async fn deregister_agent(&self, id: &str) -> Result<()> {
        self.get_agent(id).await?;
        let _: Option<serde_json::Value> = self.db.delete(("agents", id)).await?;
        Ok(())
    }

    /// Get agent health status
    pub async fn check_agent_health(&self, id: &str) -> Result<bool> {
        let agent = self.get_agent(id).await?;
        Ok(agent.status == AgentStatus::Active)
    }

    /// Get agents available for task assignment
    pub async fn get_available_agents(&self) -> Result<Vec<Agent>> {
        let mut response = self
            .db
            .query("SELECT * FROM agents WHERE status = 'active' ORDER BY role ASC")
            .await?;

        let raw: Vec<serde_json::Value> = response.take(0)?;
        Ok(raw
            .into_iter()
            .filter_map(|v| serde_json::from_value(v).ok())
            .collect())
    }
}

#[cfg(test)]
mod tests {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn test_agent_service_creation() {}
}
