// Project service - CRUD operations for projects

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> {
        // Set timestamps
        let now = Utc::now();
        project.created_at = now;
        project.updated_at = now;

        // Create project in database
        let created: Option<Project> = self
            .db
            .create("projects")
            .content(project)
            .await?
            .into_iter()
            .next();

        created.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 projects: Vec<Project> = response.take(0)?;
        Ok(projects)
    }

    /// List projects by status for a tenant
    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 projects: Vec<Project> = response.take(0)?;
        Ok(projects)
    }

    /// Get a project by ID
    pub async fn get_project(&self, id: &str, tenant_id: &str) -> Result<Project> {
        let project: Option<Project> = self.db.select(("projects", id)).await?;

        let project = project
            .ok_or_else(|| VaporaError::NotFound(format!("Project with id '{}' not found", id)))?;

        // Verify tenant ownership
        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> {
        // Verify project exists and belongs to tenant
        let existing = self.get_project(id, tenant_id).await?;

        // Preserve certain fields
        updates.id = existing.id;
        updates.tenant_id = existing.tenant_id;
        updates.created_at = existing.created_at;
        updates.updated_at = Utc::now();

        // Update in database
        let updated: Option<Project> = self.db.update(("projects", id)).content(updates).await?;

        updated.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<()> {
        // Verify project exists and belongs to tenant
        self.get_project(id, tenant_id).await?;

        // Delete from database
        let _: Option<Project> = 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?;

        // Add feature if not already present
        if !project.features.contains(&feature) {
            project.features.push(feature);
            project.updated_at = Utc::now();

            let updated: Option<Project> = self
                .db
                .update(("projects", id))
                .merge(serde_json::json!({
                    "features": project.features,
                    "updated_at": project.updated_at
                }))
                .await?;

            return updated
                .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?;

        // Remove feature
        project.features.retain(|f| f != feature);
        project.updated_at = Utc::now();

        let updated: Option<Project> = self
            .db
            .update(("projects", id))
            .merge(serde_json::json!({
                "features": project.features,
                "updated_at": project.updated_at
            }))
            .await?;

        updated.ok_or_else(|| VaporaError::DatabaseError("Failed to remove feature".to_string()))
    }

    /// Archive a project (set status to archived)
    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 updated: Option<Project> = self
            .db
            .update(("projects", id))
            .merge(serde_json::json!({
                "status": project.status,
                "updated_at": project.updated_at
            }))
            .await?;

        updated.ok_or_else(|| VaporaError::DatabaseError("Failed to archive project".to_string()))
    }
}

#[cfg(test)]
mod tests {
    use vapora_shared::models::ProjectStatus;

    use super::*;

    // Note: These are placeholder tests. Real tests require a running SurrealDB
    // instance or mocking. For Phase 1, we'll add integration tests that use a
    // test database.

    #[test]
    fn test_project_service_creation() {
        // This test just verifies the service can be created
        // Real database tests will be in integration tests
    }
}
