use std::sync::Arc;

use async_nats::Client as NatsClient;
use dashmap::DashMap;
use futures::StreamExt;
use serde_json::json;
use tokio::sync::oneshot;
use tracing::{error, info, warn};
use vapora_agents::{coordinator::AgentCoordinator, messages::AgentMessage};

use crate::{
    error::{A2aError, Result},
    metrics::{A2A_COORDINATOR_ASSIGNMENTS, A2A_NATS_MESSAGES, A2A_TASKS_TOTAL},
    protocol::{A2aMessage, A2aMessagePart, A2aTask, A2aTaskResult, TaskState},
    task_manager::TaskManager,
};

pub struct CoordinatorBridge {
    coordinator: Arc<AgentCoordinator>,
    task_manager: Arc<TaskManager>,
    result_channels: Arc<DashMap<String, oneshot::Sender<A2aTaskResult>>>,
    nats_client: Option<NatsClient>,
}

impl CoordinatorBridge {
    pub fn new(
        coordinator: Arc<AgentCoordinator>,
        task_manager: Arc<TaskManager>,
        nats_client: Option<NatsClient>,
    ) -> Self {
        Self {
            coordinator,
            task_manager,
            result_channels: Arc::new(DashMap::new()),
            nats_client,
        }
    }

    /// Start background NATS listeners for task completion/failure events
    pub async fn start_result_listener(&self) -> Result<()> {
        let Some(nats) = &self.nats_client else {
            warn!("NATS client not configured, result listener disabled");
            return Ok(());
        };

        // Subscribe to completion events
        let completed_sub = nats
            .subscribe("vapora.tasks.completed")
            .await
            .map_err(|e| A2aError::InternalError(format!("Failed to subscribe to NATS: {}", e)))?;

        let failed_sub = nats
            .subscribe("vapora.tasks.failed")
            .await
            .map_err(|e| A2aError::InternalError(format!("Failed to subscribe to NATS: {}", e)))?;

        // Spawn listener for completed tasks
        Self::spawn_completed_listener(
            completed_sub,
            self.task_manager.clone(),
            self.result_channels.clone(),
        );

        // Spawn listener for failed tasks
        Self::spawn_failed_listener(
            failed_sub,
            self.task_manager.clone(),
            self.result_channels.clone(),
        );

        info!(
            "A2A result listener started (subscribed to vapora.tasks.completed, \
             vapora.tasks.failed)"
        );
        Ok(())
    }

    fn spawn_completed_listener(
        mut completed_sub: async_nats::Subscriber,
        task_manager: Arc<TaskManager>,
        result_channels: Arc<DashMap<String, oneshot::Sender<A2aTaskResult>>>,
    ) {
        tokio::spawn(async move {
            while let Some(msg) = completed_sub.next().await {
                Self::handle_completed_message(msg, &task_manager, &result_channels).await;
            }
        });
    }

    async fn handle_completed_message(
        msg: async_nats::Message,
        task_manager: &TaskManager,
        result_channels: &DashMap<String, oneshot::Sender<A2aTaskResult>>,
    ) {
        match serde_json::from_slice::<AgentMessage>(&msg.payload) {
            Ok(AgentMessage::TaskCompleted(task_completed)) => {
                let task_id = task_completed.task_id.clone();

                // Build A2aTaskResult
                let artifacts = Self::convert_artifacts(&task_completed.artifacts);
                let result = A2aTaskResult {
                    message: A2aMessage {
                        role: "assistant".to_string(),
                        parts: vec![A2aMessagePart::Text(task_completed.result.clone())],
                    },
                    artifacts,
                };

                // Update DB
                if let Err(e) = task_manager.complete(&task_id, result.clone()).await {
                    error!(
                        error = %e,
                        task_id = %task_id,
                        "Failed to mark task as completed in DB"
                    );
                    A2A_NATS_MESSAGES
                        .with_label_values(&["completed", "db_error"])
                        .inc();
                } else {
                    A2A_NATS_MESSAGES
                        .with_label_values(&["completed", "success"])
                        .inc();
                    A2A_TASKS_TOTAL.with_label_values(&["completed"]).inc();
                }

                // Send to waiting channel if exists
                Self::send_to_channel(&task_id, result, result_channels);
            }
            Ok(_) => {
                warn!("Received non-TaskCompleted message on vapora.tasks.completed");
                A2A_NATS_MESSAGES
                    .with_label_values(&["completed", "wrong_type"])
                    .inc();
            }
            Err(e) => {
                warn!(error = %e, "Failed to deserialize TaskCompleted message");
                A2A_NATS_MESSAGES
                    .with_label_values(&["completed", "deserialize_error"])
                    .inc();
            }
        }
    }

    fn spawn_failed_listener(
        mut failed_sub: async_nats::Subscriber,
        task_manager: Arc<TaskManager>,
        result_channels: Arc<DashMap<String, oneshot::Sender<A2aTaskResult>>>,
    ) {
        tokio::spawn(async move {
            while let Some(msg) = failed_sub.next().await {
                Self::handle_failed_message(msg, &task_manager, &result_channels).await;
            }
        });
    }

    async fn handle_failed_message(
        msg: async_nats::Message,
        task_manager: &TaskManager,
        result_channels: &DashMap<String, oneshot::Sender<A2aTaskResult>>,
    ) {
        match serde_json::from_slice::<AgentMessage>(&msg.payload) {
            Ok(AgentMessage::TaskFailed(task_failed)) => {
                let task_id = task_failed.task_id.clone();

                // Update DB with error
                let error_obj = crate::protocol::A2aErrorObj {
                    code: -1,
                    message: task_failed.error.clone(),
                };

                if let Err(e) = task_manager.fail(&task_id, error_obj).await {
                    error!(
                        error = %e,
                        task_id = %task_id,
                        "Failed to mark task as failed in DB"
                    );
                    A2A_NATS_MESSAGES
                        .with_label_values(&["failed", "db_error"])
                        .inc();
                } else {
                    A2A_NATS_MESSAGES
                        .with_label_values(&["failed", "success"])
                        .inc();
                    A2A_TASKS_TOTAL.with_label_values(&["failed"]).inc();
                }

                // Remove waiting channel (task failed, no result to send)
                result_channels.remove(&task_id);
            }
            Ok(_) => {
                warn!("Received non-TaskFailed message on vapora.tasks.failed");
                A2A_NATS_MESSAGES
                    .with_label_values(&["failed", "wrong_type"])
                    .inc();
            }
            Err(e) => {
                warn!(error = %e, "Failed to deserialize TaskFailed message");
                A2A_NATS_MESSAGES
                    .with_label_values(&["failed", "deserialize_error"])
                    .inc();
            }
        }
    }

    fn convert_artifacts(artifacts: &[String]) -> Option<Vec<crate::protocol::A2aArtifact>> {
        if artifacts.is_empty() {
            return None;
        }

        Some(
            artifacts
                .iter()
                .map(|path| crate::protocol::A2aArtifact {
                    artifact_type: "file".to_string(),
                    format: None,
                    title: Some(
                        std::path::Path::new(path)
                            .file_name()
                            .and_then(|n| n.to_str())
                            .unwrap_or(path)
                            .to_string(),
                    ),
                    data: json!({ "path": path }),
                })
                .collect(),
        )
    }

    fn send_to_channel(
        task_id: &str,
        result: A2aTaskResult,
        result_channels: &DashMap<String, oneshot::Sender<A2aTaskResult>>,
    ) {
        if let Some((_, sender)) = result_channels.remove(task_id) {
            if sender.send(result).is_err() {
                warn!(
                    task_id = %task_id,
                    "Failed to send result to channel (receiver dropped)"
                );
            }
        }
    }

    pub async fn dispatch(&self, a2a_task: A2aTask) -> Result<String> {
        let task_id = a2a_task.id.clone();

        let skill = a2a_task
            .metadata
            .get("skill")
            .and_then(|v| v.as_str())
            .unwrap_or("developer")
            .to_string();

        let task_text = a2a_task
            .message
            .parts
            .iter()
            .filter_map(|p| match p {
                A2aMessagePart::Text(t) => Some(t.clone()),
                _ => None,
            })
            .collect::<Vec<_>>()
            .join("\n");

        if task_text.is_empty() {
            return Err(A2aError::InternalError(
                "No text in task message".to_string(),
            ));
        }

        let parts: Vec<&str> = task_text.splitn(2, '\n').collect();
        let title = parts[0].to_string();
        let description = parts.get(1).unwrap_or(&"").to_string();

        // Create task in DB (status: waiting)
        self.task_manager.create(a2a_task).await?;
        A2A_TASKS_TOTAL.with_label_values(&["waiting"]).inc();

        // Update status to working
        self.task_manager
            .update_state(&task_id, TaskState::Working)
            .await?;
        A2A_TASKS_TOTAL.with_label_values(&["working"]).inc();

        // Assign to agent (via AgentCoordinator)
        match self
            .coordinator
            .assign_task(&skill, title, description, json!({}).to_string(), 50)
            .await
        {
            Ok(_) => {
                A2A_COORDINATOR_ASSIGNMENTS
                    .with_label_values(&[skill.as_str(), "success"])
                    .inc();
                info!("Task {} dispatched to coordinator", task_id);
            }
            Err(e) => {
                A2A_COORDINATOR_ASSIGNMENTS
                    .with_label_values(&[skill.as_str(), "error"])
                    .inc();
                return Err(A2aError::CoordinatorError(e.to_string()));
            }
        }

        // NO sleep(5) here! Result will come via NATS subscriber

        Ok(task_id)
    }

    pub async fn get_task(&self, id: &str) -> Result<crate::protocol::A2aTaskStatus> {
        self.task_manager.get(id).await
    }
}
