use chrono::Utc;
use surrealdb::engine::remote::ws::Client;
use surrealdb::Surreal;
use vapora_shared::models::{Task, TaskPriority, TaskStatus};
use vapora_shared::{Result, VaporaError};

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

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

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

        if task.task_order == 0 {
            let max_order = self
                .get_max_task_order(&task.project_id, &task.status)
                .await?;
            task.task_order = max_order + 1;
        }

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

    /// List all tasks for a project
    pub async fn list_tasks(&self, project_id: &str, tenant_id: &str) -> Result<Vec<Task>> {
        let mut response = self
            .db
            .query(
                "SELECT * FROM tasks WHERE project_id = $project_id AND tenant_id = $tenant_id \
                 ORDER BY task_order ASC",
            )
            .bind(("project_id", project_id.to_string()))
            .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 tasks by status (for Kanban columns)
    pub async fn list_tasks_by_status(
        &self,
        project_id: &str,
        tenant_id: &str,
        status: TaskStatus,
    ) -> Result<Vec<Task>> {
        let status_str = match status {
            TaskStatus::Todo => "todo",
            TaskStatus::Doing => "doing",
            TaskStatus::Review => "review",
            TaskStatus::Done => "done",
        };

        let mut response = self
            .db
            .query(
                "SELECT * FROM tasks WHERE project_id = $project_id AND tenant_id = $tenant_id \
                 AND status = $status ORDER BY task_order ASC",
            )
            .bind(("project_id", project_id.to_string()))
            .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())
    }

    /// List tasks by assignee
    pub async fn list_tasks_by_assignee(
        &self,
        project_id: &str,
        tenant_id: &str,
        assignee: &str,
    ) -> Result<Vec<Task>> {
        let mut response = self
            .db
            .query(
                "SELECT * FROM tasks WHERE project_id = $project_id AND tenant_id = $tenant_id \
                 AND assignee = $assignee ORDER BY priority DESC, task_order ASC",
            )
            .bind(("project_id", project_id.to_string()))
            .bind(("tenant_id", tenant_id.to_string()))
            .bind(("assignee", assignee.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 task by ID
    pub async fn get_task(&self, id: &str, tenant_id: &str) -> Result<Task> {
        let raw: Option<serde_json::Value> = self.db.select(("tasks", id)).await?;
        let task: Task = raw
            .and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::NotFound(format!("Task with id '{}' not found", id)))?;

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

        Ok(task)
    }

    /// Update a task
    pub async fn update_task(&self, id: &str, tenant_id: &str, mut updates: Task) -> Result<Task> {
        let existing = self.get_task(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(("tasks", id)).content(json).await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to update task".to_string()))
    }

    /// Update task status (for Kanban column changes)
    pub async fn update_task_status(
        &self,
        id: &str,
        tenant_id: &str,
        status: TaskStatus,
    ) -> Result<Task> {
        let task = self.get_task(id, tenant_id).await?;
        let max_order = self.get_max_task_order(&task.project_id, &status).await?;

        let raw: Option<serde_json::Value> = self
            .db
            .update(("tasks", id))
            .merge(serde_json::json!({
                "status": status,
                "task_order": max_order + 1,
                "updated_at": Utc::now()
            }))
            .await?;
        raw.and_then(|v| serde_json::from_value(v).ok())
            .ok_or_else(|| VaporaError::DatabaseError("Failed to update task status".to_string()))
    }

    /// Reorder task (for drag & drop in Kanban)
    pub async fn reorder_task(
        &self,
        id: &str,
        tenant_id: &str,
        new_order: i32,
        new_status: Option<TaskStatus>,
    ) -> Result<Task> {
        let mut task = self.get_task(id, tenant_id).await?;

        if let Some(status) = new_status {
            task.status = status;
        }

        task.task_order = new_order;
        task.updated_at = Utc::now();

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

    /// Assign task to agent/user
    pub async fn assign_task(&self, id: &str, tenant_id: &str, assignee: String) -> Result<Task> {
        let mut task = self.get_task(id, tenant_id).await?;
        task.assignee = assignee;
        task.updated_at = Utc::now();

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

    /// Update task priority
    pub async fn update_priority(
        &self,
        id: &str,
        tenant_id: &str,
        priority: TaskPriority,
    ) -> Result<Task> {
        let mut task = self.get_task(id, tenant_id).await?;
        task.priority = priority;
        task.updated_at = Utc::now();

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

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

    /// Get maximum task order for a project/status combination
    async fn get_max_task_order(&self, project_id: &str, status: &TaskStatus) -> Result<i32> {
        let status_str = match status {
            TaskStatus::Todo => "todo",
            TaskStatus::Doing => "doing",
            TaskStatus::Review => "review",
            TaskStatus::Done => "done",
        };

        let mut response = self
            .db
            .query(
                "SELECT VALUE task_order FROM tasks WHERE project_id = $project_id AND status = \
                 $status ORDER BY task_order DESC LIMIT 1",
            )
            .bind(("project_id", project_id.to_string()))
            .bind(("status", status_str.to_string()))
            .await?;

        let raw: Vec<serde_json::Value> = response.take(0)?;
        Ok(raw
            .into_iter()
            .find_map(|v| v.as_i64().map(|n| n as i32))
            .unwrap_or(0))
    }
}

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

    #[test]
    fn test_task_service_creation() {}
}
