use chrono::{DateTime, Utc};
use models::rooms::{
    MessageContentType, MessageSenderType, room_message::Model as RoomMessageModel,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;

use crate::tokent::TokenUsage;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageSummary {
    pub id: Uuid,
    pub sender_type: MessageSenderType,
    pub sender_id: Option<Uuid>,
    pub sender_name: String,
    pub content: String,
    pub content_type: MessageContentType,
    /// Tool call ID extracted from message content JSON, if present.
    pub tool_call_id: Option<String>,
    pub send_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactSummary {
    pub session_id: Uuid,
    pub room_id: Uuid,
    pub retained: Vec<MessageSummary>,
    pub summary: String,
    pub compacted_at: DateTime<Utc>,
    pub messages_compressed: usize,
    /// Token usage for the compaction AI call. `None` if usage data was unavailable.
    pub usage: Option<TokenUsage>,
}

#[derive(Debug, Clone, Copy)]
pub enum CompactLevel {
    Light,
    Aggressive,
}

impl CompactLevel {
    pub fn retain_count(&self) -> usize {
        match self {
            CompactLevel::Light => 5,
            CompactLevel::Aggressive => 2,
        }
    }

    /// Auto-select level based on estimated token count and config.
    ///
    /// - `Light` (retain 5): when tokens are moderately over threshold
    /// - `Aggressive` (retain 2): when tokens are severely over threshold (2x+)
    pub fn auto_select(estimated_tokens: usize, threshold: usize) -> Self {
        if threshold == 0 {
            return CompactLevel::Light;
        }
        if estimated_tokens >= threshold * 2 {
            CompactLevel::Aggressive
        } else {
            CompactLevel::Light
        }
    }
}

/// Configuration for automatic compaction.
#[derive(Debug, Clone, Copy)]
pub struct CompactConfig {
    /// Only trigger compaction when estimated token count exceeds this.
    /// Set to 0 to disable threshold (always compact when messages > retain_count).
    pub token_threshold: usize,
    /// If true, auto-select level based on how far over the threshold we are.
    /// If false, always use `default_level`.
    pub auto_level: bool,
    /// Fallback level when `auto_level` is false.
    pub default_level: CompactLevel,
}

impl Default for CompactConfig {
    fn default() -> Self {
        // Trigger when estimated tokens exceed ~8k (reasonable for a context window)
        Self {
            token_threshold: 8000,
            auto_level: true,
            default_level: CompactLevel::Light,
        }
    }
}

/// Result of a threshold check before deciding whether to compact.
#[derive(Debug)]
pub enum ThresholdResult {
    /// Token count is below threshold — skip compaction.
    Skip { estimated_tokens: usize },
    /// Token count exceeds threshold — compact with this level.
    Compact {
        estimated_tokens: usize,
        level: CompactLevel,
    },
}

impl From<RoomMessageModel> for MessageSummary {
    fn from(m: RoomMessageModel) -> Self {
        let sender_type = m.sender_type.clone();
        let content = m.content.clone();
        Self {
            id: m.id,
            sender_type: sender_type.clone(),
            sender_id: m.sender_id,
            sender_name: sender_type.to_string(),
            content,
            content_type: m.content_type.clone(),
            tool_call_id: Self::extract_tool_call_id(&m.content),
            send_at: m.send_at,
        }
    }
}

impl MessageSummary {
    fn extract_tool_call_id(content: &str) -> Option<String> {
        let content = content.trim();
        if let Ok(v) = serde_json::from_str::<Value>(content) {
            v.get("tool_call_id")
                .and_then(|v| v.as_str())
                .map(|s| s.to_string())
        } else {
            None
        }
    }
}
