use std::collections::HashMap;
use std::sync::Arc;

use anyhow::Result;
use async_trait::async_trait;
use sea_orm::DatabaseConnection;

use robson_core::plugin::{MessageEvent, ProcessEventKind, Worker};

use crate::commands::execute_run_task;
use crate::config::{WorkflowConfig, WorkflowState};

pub struct RunTaskWorker {
    workflow_db: DatabaseConnection,
    config: Arc<WorkflowConfig>,
}

impl RunTaskWorker {
    pub const PATTERN: &'static str = r"(?i)(^<@\S+>\s*)?/run-task";

    pub fn new(workflow_db: DatabaseConnection, config: Arc<WorkflowConfig>) -> Self {
        Self {
            workflow_db,
            config,
        }
    }
}

#[async_trait]
impl Worker for RunTaskWorker {
    fn name(&self) -> &'static str {
        "RunTaskWorker"
    }

    fn description(&self) -> &'static str {
        "Run the implementation pipeline for the given Jira task key."
    }

    fn example(&self) -> &'static str {
        "`/run task key=<KEY>`"
    }

    async fn handle(
        &self,
        db: DatabaseConnection,
        msg: MessageEvent,
        args: HashMap<String, String>,
    ) -> Result<bool> {
        let key = args.get("key").cloned().unwrap_or_default();

        if key.is_empty() {
            let msg_text = ":x: Missing required field: *key*\n\nUsage: `/run-task key=<JIRA-KEY> [repo_url=<git-url>]`";
            if let Some(conv_id) = msg.conversation_id {
                if let Err(e) = robson_core::entities::process_event::Model::insert(
                    &db,
                    conv_id,
                    robson_core::plugin::ProcessEventKind::Failed.as_str(),
                    msg_text,
                )
                .await
                {
                    tracing::warn!(error = %e, "failed to persist process event");
                }
            }
            return Ok(true);
        }

        let repo_url = args.get("repo_url").or_else(|| args.get("repo")).cloned();

        let state = if let Some(conv_id) = msg.conversation_id {
            WorkflowState::new_with_agent_context(
                self.workflow_db.clone(),
                (*self.config).clone(),
                db.clone(),
                conv_id,
            )
        } else {
            WorkflowState::new(self.workflow_db.clone(), (*self.config).clone())
        };

        let (kind, response) = match execute_run_task(key, repo_url, state).await {
            Ok(text) => (ProcessEventKind::Completed, text),
            Err(e) => (
                ProcessEventKind::Failed,
                format!(":x: Run task failed: {:#}", e),
            ),
        };

        if let Some(conv_id) = msg.conversation_id {
            if let Err(e) = robson_core::entities::process_event::Model::insert(
                &db,
                conv_id,
                kind.as_str(),
                &response,
            )
            .await
            {
                tracing::warn!(error = %e, "failed to persist process event");
            }
        }

        Ok(true)
    }
}
