use crate::render::utils::sanitize_terminal_text;

use std::path::Path;

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum MemoryStatus {
    #[default]
    Disabled,
    Unknown,
    Connected,
    Degraded,
}

impl MemoryStatus {
    pub(crate) fn label(self) -> &'static str {
        match self {
            Self::Disabled => "OFF",
            Self::Unknown => "UNKNOWN",
            Self::Connected => "OK",
            Self::Degraded => "DEGRADED",
        }
    }
}

pub struct StatusPanel {
    pub model_name: String,
    pub provider_name: String,
    /// Shortened display string for current agent workspace (tools cwd).
    pub workspace_display: String,
    pub backend_display: String,
    pub total_tokens: u64,
    pub prompt_tokens: u64,
    pub completion_tokens: u64,
    pub last_latency_ms: u64,
    pub is_connected: bool,
    pub input_context_tokens: u64,
    pub input_context_tokens_estimated: bool,
    pub memory_status: MemoryStatus,
}

impl Default for StatusPanel {
    fn default() -> Self {
        Self {
            model_name: String::new(),
            provider_name: String::new(),
            workspace_display: String::new(),
            backend_display: "Local".to_string(),
            total_tokens: 0,
            prompt_tokens: 0,
            completion_tokens: 0,
            last_latency_ms: 0,
            is_connected: false,
            input_context_tokens: 0,
            input_context_tokens_estimated: false,
            memory_status: MemoryStatus::Disabled,
        }
    }
}

impl StatusPanel {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn set_provider(&mut self, provider: &str, model: &str) {
        self.provider_name = provider.to_string();
        self.model_name = model.to_string();
        self.is_connected = true;
    }

    /// Update the workspace line from an absolute path (caller should pass canonical path when possible).
    pub fn set_workspace(&mut self, path: &Path) {
        self.workspace_display = shorten_path_display(path, 26);
    }

    pub fn set_remote_workspace(&mut self, base_url: &str) {
        self.workspace_display = format!("remote:{base_url}");
    }

    pub fn set_backend(&mut self, backend: impl Into<String>) {
        self.backend_display = backend.into();
    }

    pub fn update_metrics(
        &mut self,
        prompt_tokens: u64,
        completion_tokens: u64,
        latency_ms: u64,
        input_context_tokens: u64,
        input_context_tokens_estimated: bool,
    ) {
        self.input_context_tokens = input_context_tokens;
        self.input_context_tokens_estimated = input_context_tokens_estimated;
        self.prompt_tokens = self.prompt_tokens.saturating_add(prompt_tokens);
        self.completion_tokens = self.completion_tokens.saturating_add(completion_tokens);
        self.total_tokens = self
            .total_tokens
            .saturating_add(prompt_tokens.saturating_add(completion_tokens));
        self.last_latency_ms = latency_ms;
    }

    pub(crate) fn format_token_count(tokens: u64) -> String {
        if tokens < 1000 {
            format!("{}", tokens)
        } else if tokens < 1_000_000 {
            format!("{:.1}K", tokens as f64 / 1000.0)
        } else {
            format!("{:.1}M", tokens as f64 / 1_000_000.0)
        }
    }

    pub(crate) fn format_context_usage(input_tokens: u64, input_tokens_estimated: bool) -> String {
        let prefix = if input_tokens_estimated { "~" } else { "" };
        let usage = Self::format_token_count(input_tokens);
        format!("{prefix}{usage}")
    }
}

fn shorten_path_display(path: &Path, max_chars: usize) -> String {
    let s = path.to_string_lossy();
    let count = s.chars().count();
    if count <= max_chars {
        return s.into_owned();
    }
    let prefix = sanitize_terminal_text("…");
    let keep = max_chars.saturating_sub(prefix.chars().count());
    let skip = count.saturating_sub(keep);
    format!("{prefix}{}", s.chars().skip(skip).collect::<String>())
}

#[cfg(test)]
mod tests {
    use super::{MemoryStatus, StatusPanel};

    #[test]
    fn memory_status_labels_are_operator_readable() {
        assert_eq!(MemoryStatus::Unknown.label(), "UNKNOWN");
        assert_eq!(MemoryStatus::Connected.label(), "OK");
        assert_eq!(MemoryStatus::Degraded.label(), "DEGRADED");
    }

    #[test]
    fn format_context_usage_marks_estimated_values() {
        assert_eq!(StatusPanel::format_context_usage(24_000, true), "~24.0K");
    }

    #[test]
    fn format_context_usage_leaves_reported_values_unmarked() {
        assert_eq!(StatusPanel::format_context_usage(24_000, false), "24.0K");
    }
}