use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SessionStats {
    pub session_id: String,
    pub title: String,
    pub provider: String,
    pub model: String,
    pub user_message_count: u64,
    pub history_message_count: u64,
    pub lifetime_usage: LifetimeUsage,
    pub context: ContextUsage,
    pub created_at: i64,
    pub last_active_at: i64,
    pub updated_at: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct LifetimeUsage {
    pub input_tokens: u64,
    pub output_tokens: u64,
    pub total_tokens: u64,
    pub cached_input_tokens: Option<u64>,
    pub request_count: u64,
    pub turn_count: u64,
    pub tracked_since: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ContextUsage {
    pub configured_window_tokens: u64,
    pub effective_window_tokens: u64,
    pub configured_reserve_tokens: u64,
    pub reserve_tokens: u64,
    pub configured_keep_recent_tokens: u64,
    pub effective_keep_recent_tokens: u64,
    pub used_tokens: u64,
    pub remaining_tokens: u64,
    pub compression_threshold_tokens: u64,
    pub source: ContextUsageSource,
    pub last_observed_prompt_tokens: Option<u64>,
    pub observed_at: Option<i64>,
    pub active_checkpoint_id: Option<String>,
    pub checkpoint_generation: u64,
    pub checkpoint_tokens_before: Option<u64>,
    pub checkpoint_tokens_after: Option<u64>,
    pub checkpoint_degraded: bool,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ContextUsageSource {
    Observed,
    Estimated,
}

impl ContextUsageSource {
    pub fn label(self) -> &'static str {
        match self {
            Self::Observed => "Provider 实测",
            Self::Estimated => "字符估算",
        }
    }
}

impl SessionStats {
    pub fn render_text(&self) -> String {
        let percent = if self.context.effective_window_tokens == 0 {
            0.0
        } else {
            self.context.used_tokens as f64 / self.context.effective_window_tokens as f64 * 100.0
        };
        let created_at = format_timestamp(self.created_at);
        let last_active_at = format_timestamp(self.last_active_at);
        let tracked_since = self
            .lifetime_usage
            .tracked_since
            .map(format_timestamp)
            .unwrap_or_else(|| "尚无已完成模型请求".to_string());
        let cached = self
            .lifetime_usage
            .cached_input_tokens
            .map(format_tokens)
            .unwrap_or_else(|| "—".to_string());
        let observed = self
            .context
            .last_observed_prompt_tokens
            .map(format_tokens)
            .unwrap_or_else(|| "—".to_string());
        let checkpoint = self
            .context
            .active_checkpoint_id
            .as_deref()
            .map(|id| {
                let before = self
                    .context
                    .checkpoint_tokens_before
                    .map(format_tokens)
                    .unwrap_or_else(|| "—".to_string());
                let after = self
                    .context
                    .checkpoint_tokens_after
                    .map(format_tokens)
                    .unwrap_or_else(|| "—".to_string());
                let degraded = if self.context.checkpoint_degraded {
                    "，overflow 降级"
                } else {
                    ""
                };
                format!(
                    "{}（generation {}，{} → {}{}）",
                    id, self.context.checkpoint_generation, before, after, degraded
                )
            })
            .unwrap_or_else(|| "—".to_string());

        format!(
            "会话\n  标题        {}\n  ID          {}\n  模型        {} / {}\n  消息        {} 条用户消息，{} 条历史消息\n  创建        {}\n  最后活跃    {}\n\nToken 用量 · 已提交 Turns\n  输入        {}\n  输出        {}\n  合计        {}\n  缓存输入    {}\n  请求        {}\n  Turns       {}\n  统计起点    {}\n\n上下文窗口 · {}\n  占用        {} / {}（{:.1}%）\n  剩余        {}\n  预留        {}（配置 {}）\n  近期保留    {}（配置 {}）\n  自动压缩阈值 {}\n  最近实测    {}\n  Checkpoint  {}",
            self.title,
            self.session_id,
            self.provider,
            self.model,
            self.user_message_count,
            self.history_message_count,
            created_at,
            last_active_at,
            format_tokens(self.lifetime_usage.input_tokens),
            format_tokens(self.lifetime_usage.output_tokens),
            format_tokens(self.lifetime_usage.total_tokens),
            cached,
            format_tokens(self.lifetime_usage.request_count),
            format_tokens(self.lifetime_usage.turn_count),
            tracked_since,
            self.context.source.label(),
            format_tokens(self.context.used_tokens),
            format_tokens(self.context.effective_window_tokens),
            percent,
            format_tokens(self.context.remaining_tokens),
            format_tokens(self.context.reserve_tokens),
            format_tokens(self.context.configured_reserve_tokens),
            format_tokens(self.context.effective_keep_recent_tokens),
            format_tokens(self.context.configured_keep_recent_tokens),
            format_tokens(self.context.compression_threshold_tokens),
            observed,
            checkpoint,
        )
    }
}

fn format_timestamp(value: i64) -> String {
    chrono::DateTime::from_timestamp_millis(value)
        .map(|timestamp| {
            timestamp
                .with_timezone(&chrono::Local)
                .format("%Y-%m-%d %H:%M:%S")
                .to_string()
        })
        .unwrap_or_else(|| "—".to_string())
}

fn format_tokens(value: u64) -> String {
    let digits = value.to_string();
    let mut formatted = String::with_capacity(digits.len() + digits.len() / 3);
    for (index, ch) in digits.chars().enumerate() {
        if index > 0 && (digits.len() - index).is_multiple_of(3) {
            formatted.push(',');
        }
        formatted.push(ch);
    }
    formatted
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn formats_large_token_counts() {
        assert_eq!(format_tokens(1_234_567), "1,234,567");
        assert_eq!(format_tokens(12), "12");
    }
}
