use chrono::Utc;
use surrealdb::engine::remote::ws::Client;
use surrealdb::Surreal;
use vapora_shared::models::{Project, ProjectStatus};
use vapora_shared::{Result, VaporaError};

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

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

    /// Create a new project
    pub async fn create_project(&self, mut project: Project) -> Result<Project> {
        let now = Utc::now();
        project.created_at = now;
        project.updated_at = now;

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

    /// List all projects for a tenant
    pub async fn list_projects(&self, tenant_id: &str) -> Result<Vec<Project>> {
        let mut response = self
            .db
            .query("SELECT * FROM projects WHERE tenant_id = $tenant_id ORDER BY created_at DESC")
            .bind(("tenant_id", tenant_id.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())
    }

    /// List projects by status for a tenant
    #[allow(dead_code)]
    pub async fn list_projects_by_status(
        &self,
        tenant_id: &str,
        status: ProjectStatus,
    ) -> Result<Vec<Project>> {
        let status_str = match status {
            ProjectStatus::Active => "active",
            ProjectStatus::Archived => "archived",
            ProjectStatus::Completed => "completed",
        };

        let mut response = self
            .db
            .query(
                "SELECT * FROM projects WHERE tenant_id = $tenant_id AND status = $status ORDER \
                 BY created_at DESC",
            )
            .bind(("tenant_id", tenant_id.to_string()))
            .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 a project by ID
    pub async fn get_project(&self, id: &str, tenant_id: &str) -> Result<Project> {
        let raw: Option<serde_json::Value> = self.db.select(("projects", id)).await?;
        let project: Project = raw
            .and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::NotFound(format!("Project with id '{}' not found", id)))?;

        if project.tenant_id != tenant_id {
            return Err(VaporaError::Unauthorized(
                "Project does not belong to this tenant".to_string(),
            ));
        }

        Ok(project)
    }

    /// Update a project
    pub async fn update_project(
        &self,
        id: &str,
        tenant_id: &str,
        mut updates: Project,
    ) -> Result<Project> {
        let existing = self.get_project(id, tenant_id).await?;
        updates.id = existing.id;
        updates.tenant_id = existing.tenant_id;
        updates.created_at = existing.created_at;
        updates.updated_at = Utc::now();

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

    /// Delete a project
    pub async fn delete_project(&self, id: &str, tenant_id: &str) -> Result<()> {
        self.get_project(id, tenant_id).await?;
        let _: Option<serde_json::Value> = self.db.delete(("projects", id)).await?;
        Ok(())
    }

    /// Add a feature to a project
    pub async fn add_feature(&self, id: &str, tenant_id: &str, feature: String) -> Result<Project> {
        let mut project = self.get_project(id, tenant_id).await?;

        if !project.features.contains(&feature) {
            project.features.push(feature);
            project.updated_at = Utc::now();

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

        Ok(project)
    }

    /// Remove a feature from a project
    pub async fn remove_feature(
        &self,
        id: &str,
        tenant_id: &str,
        feature: &str,
    ) -> Result<Project> {
        let mut project = self.get_project(id, tenant_id).await?;
        project.features.retain(|f| f != feature);
        project.updated_at = Utc::now();

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

    /// Archive a project
    pub async fn archive_project(&self, id: &str, tenant_id: &str) -> Result<Project> {
        let mut project = self.get_project(id, tenant_id).await?;
        project.status = ProjectStatus::Archived;
        project.updated_at = Utc::now();

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

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

    #[test]
    fn test_project_service_creation() {}
}
