//! Chat state provider for BlueBubbles

use crate::service::BlueBubblesService;
use crate::types::BlueBubblesChatState;
use async_trait::async_trait;
use elizaos::{Memory, ProviderDefinition, ProviderHandler, ProviderResult, State};
use std::sync::Arc;
use tracing::debug;

/// Provider that supplies BlueBubbles chat context
pub struct ChatStateProvider {
    service: Arc<BlueBubblesService>,
}

impl ChatStateProvider {
    /// Creates a new chat state provider
    pub fn new(service: Arc<BlueBubblesService>) -> Self {
        Self { service }
    }
}

#[async_trait]
impl ProviderHandler for ChatStateProvider {
    fn definition(&self) -> ProviderDefinition {
        ProviderDefinition {
            name: "BLUEBUBBLES_CHAT_STATE".to_string(),
            description: Some(
                "Provides information about the current BlueBubbles/iMessage chat context"
                    .to_string(),
            ),
            dynamic: Some(true),
            position: None,
            private: None,
        }
    }

    async fn get(
        &self,
        message: &Memory,
        state: &State,
    ) -> std::result::Result<ProviderResult, anyhow::Error> {
        // Only provide state for BlueBubbles messages
        if message.content.source.as_deref() != Some("bluebubbles") {
            return Ok(ProviderResult::empty());
        }

        // Try to get chat GUID from message content data or state values
        let chat_guid = message
            .content
            .data
            .as_ref()
            .and_then(|d| d.get("chatGuid"))
            .and_then(|v| v.as_str())
            .map(String::from)
            .or_else(|| {
                state
                    .get_value("chatGuid")
                    .and_then(|v| v.as_str().map(String::from))
            });

        let chat_guid = match chat_guid {
            Some(guid) => guid,
            None => {
                debug!("No chat GUID found for BlueBubbles provider");
                return Ok(ProviderResult::empty());
            }
        };

        let chat_state = match self.service.get_chat_state(&chat_guid).await {
            Ok(Some(state)) => state,
            Ok(None) => {
                debug!("No chat state found for {}", chat_guid);
                return Ok(ProviderResult::empty());
            }
            Err(e) => {
                debug!("Failed to get chat state: {}", e);
                return Ok(ProviderResult::empty());
            }
        };

        let text = format_chat_state(&chat_state);
        Ok(ProviderResult::with_text(text)
            .with_value("chatGuid", serde_json::json!(chat_state.chat_guid))
            .with_value(
                "chatIdentifier",
                serde_json::json!(chat_state.chat_identifier),
            ))
    }
}

/// Formats the chat state for inclusion in prompts
fn format_chat_state(state: &BlueBubblesChatState) -> String {
    let mut lines = vec![
        "# iMessage Chat Context (BlueBubbles)".to_string(),
        String::new(),
        format!(
            "- Chat Type: {}",
            if state.is_group {
                "Group Chat"
            } else {
                "Direct Message"
            }
        ),
    ];

    if let Some(ref name) = state.display_name {
        lines.push(format!("- Chat Name: {}", name));
    }

    if state.is_group {
        lines.push(format!("- Participants: {}", state.participants.join(", ")));
    } else {
        lines.push(format!(
            "- Contact: {}",
            state
                .participants
                .first()
                .unwrap_or(&state.chat_identifier)
        ));
    }

    if let Some(timestamp) = state.last_message_at {
        // Format timestamp - in production use chrono for proper formatting
        lines.push(format!("- Last Message: {} (timestamp)", timestamp));
    }

    if state.has_unread {
        lines.push("- Has Unread Messages: Yes".to_string());
    }

    lines.push(String::new());
    lines.push(
        "Note: This conversation is happening through iMessage. Be conversational and friendly."
            .to_string(),
    );

    lines.join("\n")
}
