use crate::llm::{LLMAdapter, LLMRequest};
use lumina_engine::{Event, EventBus, EventError, EventHandler, EventType};
use std::sync::Arc;
use tracing::{debug, error, info};

/// Event-driven LLM handler.
///
/// Listens for `LLMCallRequested` events, calls the LLM adapter, and emits
/// `LLMCallCompleted`/`LLMCallFailed` so agents never hang waiting for a
/// response that never comes.
pub struct LLMEventHandler {
    llm_adapter: Arc<dyn LLMAdapter>,
    event_bus: Arc<EventBus>,
}

impl LLMEventHandler {
    pub fn new(llm_adapter: Arc<dyn LLMAdapter>, event_bus: Arc<EventBus>) -> Self {
        Self {
            llm_adapter,
            event_bus,
        }
    }
}

#[async_trait::async_trait]
impl EventHandler for LLMEventHandler {
    async fn handle(&self, event: &Event) -> Result<Vec<Event>, EventError> {
        if event.event_type != EventType::LLMCallRequested {
            return Ok(Vec::new());
        }

        info!("LLM handler received LLMCallRequested event: {}", event.id);

        let agent_id = event.source.clone();
        let payload = &event.payload;

        let prompt = payload
            .get("prompt")
            .and_then(|v| v.as_str())
            .ok_or_else(|| {
                EventError::HandlerError("Missing prompt in LLMCallRequested event".to_string())
            })?
            .to_string();

        let step_id = payload
            .get("step_id")
            .and_then(|v| v.as_str())
            .unwrap_or("unknown")
            .to_string();

        let seed = payload
            .get("seed")
            .and_then(|v| v.as_u64())
            .unwrap_or(event.seed);

        let llm_request = LLMRequest {
            prompt: prompt.clone(),
            system_prompt: payload
                .get("system_prompt")
                .and_then(|v| v.as_str())
                .map(|s| s.to_string()),
            temperature: payload
                .get("temperature")
                .and_then(|v| v.as_f64())
                .unwrap_or(0.7),
            max_tokens: payload
                .get("max_tokens")
                .and_then(|v| v.as_u64())
                .map(|u| u as u32),
            seed,
            stop_sequences: payload
                .get("stop_sequences")
                .and_then(|v| v.as_array())
                .map(|arr| {
                    arr.iter()
                        .filter_map(|v| v.as_str().map(|s| s.to_string()))
                        .collect()
                })
                .unwrap_or_default(),
            metadata: payload
                .get("metadata")
                .and_then(|v| v.as_object())
                .map(|obj| {
                    obj.iter()
                        .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
                        .collect()
                })
                .unwrap_or_default(),
        };

        // Emit LLMCallStarted event.
        let started_event = Event::new(
            EventType::LLMCallStarted,
            agent_id.clone(),
            serde_json::json!({
                "step_id": step_id,
                "prompt": prompt,
            }),
            seed,
        );

        if let Err(e) = self.event_bus.publish(started_event).await {
            error!("Failed to emit LLMCallStarted event: {}", e);
        }

        debug!("Calling LLM adapter: {}", self.llm_adapter.name());
        let response = match self.llm_adapter.generate(&llm_request).await {
            Ok(resp) => resp,
            Err(e) => {
                error!("LLM generation failed: {}", e);

                let failed_event = Event::new(
                    EventType::LLMCallFailed,
                    agent_id.clone(),
                    serde_json::json!({
                        "step_id": step_id,
                        "error": e.to_string(),
                    }),
                    seed,
                );

                if let Err(e) = self.event_bus.publish(failed_event).await {
                    error!("Failed to emit LLMCallFailed event: {}", e);
                }

                return Err(EventError::HandlerError(format!(
                    "LLM generation failed: {}",
                    e
                )));
            }
        };

        // Emit completion event with metadata for subscribers.
        let completed_event = Event::new(
            EventType::LLMCallCompleted,
            agent_id.clone(),
            serde_json::json!({
                "step_id": step_id,
                "response": response.text.clone(),
                "model": response.model,
                "usage": {
                    "prompt_tokens": response.usage.prompt_tokens,
                    "completion_tokens": response.usage.completion_tokens,
                    "total_tokens": response.usage.total_tokens,
                },
                "finish_reason": response.finish_reason,
            }),
            seed,
        );

        info!("LLM call completed, emitting LLMCallCompleted event");
        if let Err(e) = self.event_bus.publish(completed_event).await {
            error!("Failed to emit LLMCallCompleted event: {}", e);
            return Err(EventError::HandlerError(
                "Failed to emit completion event".to_string(),
            ));
        }

        Ok(Vec::new())
    }

    fn interested_events(&self) -> Vec<EventType> {
        vec![EventType::LLMCallRequested]
    }
}
