//! Live statusline preview renderer using hardcoded sample data.
//!
//! Generates a preview string that looks like the real statusline but uses
//! fixed sample values (e.g., model="Opus4.6", cost="$0.42", usage=25%).
//! This lets the user see how their configuration will look before saving.
//!
//! Key functions:
//! - `render_preview(config)` -- returns the full preview string
//! - `update_preview_in_place(config, row)` -- redraws the preview line
//!   at a specific terminal row without moving the main cursor
//!
//! Called from the wizard on every config change (live preview via callbacks).

use crate::config::Config;
use crate::styles::{format_bar, format_colored};
use std::time::SystemTime;

// ── Sample data ──────────────────────────────────────────────────────

const SAMPLE_PRICES: &[(&str, u64)] = &[
    ("BTC", 73748),
    ("ETH", 2265),
    ("BNB", 612),
    ("SOL", 178),
];

// ── Public API ───────────────────────────────────────────────────────

/// Render a preview statusline bar using sample data for the given config.
///
/// Iterates through `config.effective_rows()`, rendering each enabled segment
/// with hard-coded sample values so the user can see what the statusline will
/// look like before committing to the configuration. Multiple rows are joined
/// with newlines.
#[cfg(test)]
pub fn render_preview(config: &Config) -> String {
    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();

    let rows = config.effective_rows();
    let row_strings: Vec<String> = rows.iter()
        .filter_map(|row_keys| {
            let parts: Vec<String> = row_keys.iter()
                .filter_map(|key| render_sample_segment(key, config, now))
                .collect();
            if parts.is_empty() { None } else { Some(parts.join(" ")) }
        })
        .collect();

    row_strings.join("\n")
}

/// Update the preview lines in-place at the given terminal row without
/// moving the main cursor. Renders up to 3 rows of preview content.
pub fn update_preview_in_place(config: &Config, preview_row: u16) {
    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();

    let label = crate::i18n::t("msg.preview");
    let rows = config.effective_rows();

    // Render up to 3 preview rows
    for i in 0..3u16 {
        let row_idx = i as usize;
        let row_content = if row_idx < rows.len() {
            let parts: Vec<String> = rows[row_idx]
                .iter()
                .filter_map(|key| render_sample_segment(key, config, now))
                .collect();
            if parts.is_empty() {
                String::new()
            } else {
                parts.join(" ")
            }
        } else {
            String::new()
        };

        let text = if !row_content.is_empty() {
            if i == 0 {
                format!("  \x1b[2m{}\x1b[0m {}", label, row_content)
            } else {
                // Align with the label width using spaces
                let padding = " ".repeat(label.len());
                format!("  \x1b[2m{}\x1b[0m {}", padding, row_content)
            }
        } else {
            String::new() // empty line clears the row
        };

        super::terminal::print_at(preview_row + i, &text);
    }
}

// ── Per-segment sample renderers ─────────────────────────────────────

pub fn render_sample_segment(key: &str, config: &Config, now: u64) -> Option<String> {
    match key {
        "model" => {
            let seg = &config.segments.model;
            if !seg.enabled {
                return None;
            }
            let text = if seg.icon.is_empty() {
                "Opus4.6".to_string()
            } else {
                format!("{} Opus4.6", seg.icon)
            };
            Some(format_colored(&seg.style, &text, now))
        }
        "cost" => {
            let seg = &config.segments.cost;
            if !seg.enabled {
                return None;
            }
            Some(format_colored(&seg.style, "$0.42", now))
        }
        "usage" => {
            let seg = &config.segments.usage;
            if !seg.enabled {
                return None;
            }
            let ratio = 0.25;
            let mut parts = Vec::new();
            parts.push(format_colored(&seg.style, "5h:", now));
            if seg.show_bar {
                parts.push(format_bar(
                    &seg.style,
                    &seg.bar_char,
                    seg.bar_length as usize,
                    ratio,
                    now,
                ));
            }
            if seg.show_percent {
                parts.push(format_colored(&seg.style, "25%", now));
            }
            if seg.show_reset {
                parts.push(format_colored(&seg.style, "1h43m", now));
            }
            if parts.len() <= 1 {
                None
            } else {
                Some(parts.join(" "))
            }
        }
        "usage_7d" => {
            let seg = &config.segments.usage_7d;
            if !seg.enabled {
                return None;
            }
            let ratio = 0.15;
            let mut parts = Vec::new();
            parts.push(format_colored(&seg.style, "7d:", now));
            if seg.show_bar {
                parts.push(format_bar(
                    &seg.style,
                    &seg.bar_char,
                    seg.bar_length as usize,
                    ratio,
                    now,
                ));
            }
            if seg.show_percent {
                parts.push(format_colored(&seg.style, "15%", now));
            }
            if seg.show_reset {
                parts.push(format_colored(&seg.style, "5d22h", now));
            }
            if parts.len() <= 1 {
                None
            } else {
                Some(parts.join(" "))
            }
        }
        "path" => {
            let seg = &config.segments.path;
            if !seg.enabled {
                return None;
            }
            Some(format_colored(&seg.style, "~/Desktop/web3", now))
        }
        "git" => {
            let seg = &config.segments.git;
            if !seg.enabled {
                return None;
            }
            let mut text = "main".to_string();
            if seg.show_dirty {
                text.push('*');
            }
            if seg.show_remote {
                text.push_str(" \u{2191}2\u{2193}1");
            }
            Some(format_colored(&seg.style, &text, now))
        }
        "context" => {
            let seg = &config.segments.context;
            if !seg.enabled {
                return None;
            }
            let ratio = 0.6;
            let mut parts = Vec::new();
            if seg.show_bar {
                parts.push(format_bar(
                    &seg.style,
                    &seg.bar_char,
                    seg.bar_length as usize,
                    ratio,
                    now,
                ));
            }
            if seg.show_percent {
                parts.push(format_colored(&seg.style, "60%", now));
            }
            if seg.show_size {
                parts.push(format_colored(&seg.style, "600K/1M", now));
            }
            if parts.is_empty() {
                None
            } else {
                Some(parts.join(" "))
            }
        }
        "crypto" => {
            let seg = &config.segments.crypto;
            if !seg.enabled {
                return None;
            }
            let display: Vec<String> = seg
                .coins
                .iter()
                .filter_map(|coin| {
                    SAMPLE_PRICES
                        .iter()
                        .find(|(sym, _)| *sym == coin.as_str())
                        .map(|(sym, price)| format!("{}:${}", sym, price))
                })
                .collect();
            if display.is_empty() {
                None
            } else {
                Some(format_colored(&seg.style, &display.join(" "), now))
            }
        }
        _ => None,
    }
}

// ── Tests ────────────────────────────────────────────────────────────

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

    fn strip_ansi(s: &str) -> String {
        let mut out = String::new();
        let mut in_escape = false;
        for ch in s.chars() {
            if ch == '\x1b' {
                in_escape = true;
                continue;
            }
            if in_escape {
                if ch == 'm' {
                    in_escape = false;
                }
                continue;
            }
            out.push(ch);
        }
        out
    }

    #[test]
    fn test_render_preview_with_defaults() {
        let config = Config::default();
        let result = render_preview(&config);
        assert!(!result.is_empty());
        // Should contain ANSI escape codes
        assert!(result.contains("\x1b["));
    }

    #[test]
    fn test_render_preview_all_disabled() {
        let config = Config {
            order: vec![
                "model".into(),
                "cost".into(),
                "usage".into(),
                "path".into(),
                "git".into(),
                "context".into(),
                "crypto".into(),
            ],
            segments: Segments {
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result = render_preview(&config);
        assert!(result.is_empty());
    }

    #[test]
    fn test_render_preview_model_only() {
        let config = Config {
            order: vec!["model".into()],
            segments: Segments {
                model: ModelSegment {
                    enabled: true,
                    style: "cyan".into(),
                    icon: "".into(),
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result = render_preview(&config);
        let visible = strip_ansi(&result);
        assert!(visible.contains("Opus4.6"));
    }

    #[test]
    fn test_render_preview_context_bar() {
        let config = Config {
            order: vec!["context".into()],
            segments: Segments {
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: true,
                    style: "ultrathink-gradient".into(),
                    bar_char: "shade".into(),
                    bar_length: 12,
                    show_bar: true,
                    show_percent: true,
                    show_size: true,
                },
                crypto: CryptoSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result = render_preview(&config);
        assert!(!result.is_empty());
        let visible = strip_ansi(&result);
        assert!(visible.contains("60%"));
        assert!(visible.contains("600K/1M"));
    }

    #[test]
    fn test_render_preview_usage_parts() {
        // bar only
        let config = Config {
            order: vec!["usage".into()],
            segments: Segments {
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: true,
                    style: "semantic".into(),
                    bar_char: "shade".into(),
                    bar_length: 8,
                    show_bar: true,
                    show_percent: false,
                    show_reset: false,
                    label: String::new(),
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result = render_preview(&config);
        assert!(!result.is_empty());
        let visible = strip_ansi(&result);
        // Should have bar chars but not percent or reset
        assert!(!visible.contains("25%"));
        assert!(!visible.contains("1h43m"));

        // percent + reset only (no bar)
        let config2 = Config {
            order: vec!["usage".into()],
            segments: Segments {
                usage: UsageSegment {
                    enabled: true,
                    style: "green".into(),
                    bar_char: "shade".into(),
                    bar_length: 8,
                    show_bar: false,
                    show_percent: true,
                    show_reset: true,
                    label: String::new(),
                },
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result2 = render_preview(&config2);
        let visible2 = strip_ansi(&result2);
        assert!(visible2.contains("25%"));
        assert!(visible2.contains("1h43m"));
    }

    #[test]
    fn test_render_preview_crypto_coins() {
        // Single coin (BTC)
        let config = Config {
            order: vec!["crypto".into()],
            segments: Segments {
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: true,
                    style: "green".into(),
                    refresh_interval: 60,
                    coins: vec!["BTC".into()],
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result = render_preview(&config);
        let visible = strip_ansi(&result);
        assert_eq!(visible, "BTC:$73748");

        // Multiple coins
        let config2 = Config {
            order: vec!["crypto".into()],
            segments: Segments {
                model: ModelSegment {
                    enabled: false,
                    ..Default::default()
                },
                cost: CostSegment {
                    enabled: false,
                    ..Default::default()
                },
                usage: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
                path: PathSegment {
                    enabled: false,
                    ..Default::default()
                },
                git: GitSegment {
                    enabled: false,
                    ..Default::default()
                },
                context: ContextSegment {
                    enabled: false,
                    ..Default::default()
                },
                crypto: CryptoSegment {
                    enabled: true,
                    style: "green".into(),
                    refresh_interval: 60,
                    coins: vec!["BTC".into(), "ETH".into(), "SOL".into()],
                },
                usage_7d: UsageSegment {
                    enabled: false,
                    ..Default::default()
                },
            },
            ..Default::default()
        };
        let result2 = render_preview(&config2);
        let visible2 = strip_ansi(&result2);
        assert!(visible2.contains("BTC:$73748"));
        assert!(visible2.contains("ETH:$2265"));
        assert!(visible2.contains("SOL:$178"));
    }
}
