use crate::api::{BankProfileResponse, RecallResult, RecallResponse, ReflectResponse};
use colored::*;
use hindsight_client::types::ChunkData;
use indicatif::{ProgressBar, ProgressStyle};
use std::io::{self, Write};

/// The logo as ANSI-colored text, generated by test-logo.py
const LOGO: &str = include_str!("logo.ansi");

// Gradient colors: #0074d9 -> #009296
const GRADIENT_START: (u8, u8, u8) = (0, 116, 217);  // #0074d9
const GRADIENT_END: (u8, u8, u8) = (0, 146, 150);    // #009296

/// Interpolate between two RGB colors
fn interpolate_color(start: (u8, u8, u8), end: (u8, u8, u8), t: f32) -> (u8, u8, u8) {
    (
        (start.0 as f32 + (end.0 as f32 - start.0 as f32) * t) as u8,
        (start.1 as f32 + (end.1 as f32 - start.1 as f32) * t) as u8,
        (start.2 as f32 + (end.2 as f32 - start.2 as f32) * t) as u8,
    )
}

/// Color text using gradient position (0.0 = start, 1.0 = end)
pub fn gradient(text: &str, t: f32) -> String {
    let (r, g, b) = interpolate_color(GRADIENT_START, GRADIENT_END, t);
    format!("\x1b[38;2;{};{};{}m{}\x1b[0m", r, g, b, text)
}

/// Color text with gradient start color (#0074d9)
pub fn gradient_start(text: &str) -> String {
    gradient(text, 0.0)
}

/// Color text with gradient end color (#009296)
pub fn gradient_end(text: &str) -> String {
    gradient(text, 1.0)
}

/// Color text with gradient middle color
pub fn gradient_mid(text: &str) -> String {
    gradient(text, 0.5)
}

/// Apply gradient across entire text string
pub fn gradient_text(text: &str) -> String {
    let chars: Vec<char> = text.chars().collect();
    let len = chars.len();
    if len == 0 {
        return String::new();
    }
    let mut result = String::new();
    for (i, ch) in chars.iter().enumerate() {
        if *ch == ' ' {
            result.push(' ');
        } else {
            let t = i as f32 / (len - 1).max(1) as f32;
            let (r, g, b) = interpolate_color(GRADIENT_START, GRADIENT_END, t);
            result.push_str(&format!("\x1b[38;2;{};{};{}m{}", r, g, b, ch));
        }
    }
    result.push_str("\x1b[0m");
    result
}

/// Dim/gray text
pub fn dim(text: &str) -> String {
    format!("\x1b[38;2;128;128;128m{}\x1b[0m", text)
}

pub fn get_logo() -> &'static str {
    LOGO
}

pub fn print_section_header(title: &str) {
    println!();
    println!("{}", gradient_text(&format!("━━━ {} ━━━", title)));
    println!();
}

pub fn print_fact(fact: &RecallResult, _show_activation: bool) {
    let fact_type = fact.type_.as_deref().unwrap_or("unknown");

    // Use gradient positions for different fact types
    let type_t = match fact_type {
        "world" => 0.0,
        "agent" => 0.5,
        "opinion" => 1.0,
        _ => 0.5,
    };

    println!("{}", gradient(&format!("[{}]", fact_type.to_uppercase()), type_t));
    println!("  {}", fact.text);

    // Show context if available
    if let Some(context) = &fact.context {
        println!("  {} {}", dim("context:"), dim(context));
    }

    // Show temporal information
    if let Some(occurred_start) = &fact.occurred_start {
        if let Some(occurred_end) = &fact.occurred_end {
            println!("  {} {} - {}", dim("date:"), dim(occurred_start), dim(occurred_end));
        } else {
            println!("  {} {}", dim("date:"), dim(occurred_start));
        }
    }

    // Show document ID if available
    if let Some(document_id) = &fact.document_id {
        println!("  {} {}", dim("document:"), dim(document_id));
    }

    println!();
}

pub fn print_chunk(chunk: &ChunkData) {
    println!("  {}", gradient_mid("─── Source Chunk ───"));

    // Split text into lines and indent each line
    for line in chunk.text.lines() {
        println!("  {}", line);
    }

    if chunk.truncated {
        println!("  {}", gradient_end("[Truncated due to token limit]"));
    }

    println!("  {} {} | {} {}",
        dim("Chunk ID:"),
        dim(&chunk.id),
        dim("Index:"),
        dim(&chunk.chunk_index.to_string())
    );

    println!();
}

pub fn print_search_results(response: &RecallResponse, show_trace: bool, show_chunks: bool) {
    let results = &response.results;
    print_section_header(&format!("Search Results ({})", results.len()));

    if results.is_empty() {
        println!("  {}", dim("No results found."));
    } else {
        for (i, fact) in results.iter().enumerate() {
            println!("  {}", dim(&format!("Result #{}", i + 1)));
            print_fact(fact, true);

            // Show chunk if available and requested
            if show_chunks {
                if let Some(chunk_id) = &fact.chunk_id {
                    if let Some(chunks) = &response.chunks {
                        if let Some(chunk) = chunks.get(chunk_id) {
                            print_chunk(chunk);
                        }
                    }
                }
            }
        }
    }

    if show_trace {
        if let Some(trace) = &response.trace {
            print_trace_info(trace);
        }
    }
}

pub fn print_think_response(response: &ReflectResponse) {
    print_section_header("Reflection");

    println!("{}", response.text);
    println!();

    if let Some(based_on) = &response.based_on {
        let count = based_on.memories.len();
        if count > 0 {
            println!("{}", dim(&format!("Based on {} memory units", count)));
        }
    }

    // Display structured output if present
    if let Some(structured) = &response.structured_output {
        println!();
        println!("{}", gradient_text("─── Structured Output ───"));
        println!();
        if let Ok(json) = serde_json::to_string_pretty(structured) {
            println!("{}", json);
        }
    }
}

pub fn print_trace_info(trace: &serde_json::Map<String, serde_json::Value>) {
    print_section_header("Trace");

    if let Some(time) = trace.get("total_time").and_then(|v| v.as_f64()) {
        println!("  {} {}", dim("total time:"), gradient_start(&format!("{:.2}ms", time)));
    }

    if let Some(count) = trace.get("activation_count").and_then(|v| v.as_i64()) {
        println!("  {} {}", dim("activation count:"), gradient_end(&count.to_string()));
    }

    println!();
}

pub fn print_success(message: &str) {
    println!("{}", gradient_start(message));
}

pub fn print_error(message: &str) {
    eprintln!("{} {}", "error:".bright_red().bold(), message.bright_red());
}

pub fn print_warning(message: &str) {
    println!("{} {}", gradient_end("warning:"), message);
}

pub fn print_info(message: &str) {
    println!("{}", gradient_start(message));
}

/// Animated gradient spinner that shows text with moving gradient colors
pub struct GradientSpinner {
    message: String,
    running: std::sync::Arc<std::sync::atomic::AtomicBool>,
    handle: Option<std::thread::JoinHandle<()>>,
}

impl GradientSpinner {
    pub fn new(message: &str) -> Self {
        let message = message.to_string();
        let running = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));

        let msg_clone = message.clone();
        let running_clone = running.clone();

        let handle = std::thread::spawn(move || {
            let chars: Vec<char> = msg_clone.chars().collect();
            let len = chars.len();
            let num_frames = 30;
            let mut current_frame = 0usize;

            while running_clone.load(std::sync::atomic::Ordering::Relaxed) {
                current_frame = (current_frame + 1) % num_frames;
                let offset = current_frame as f32 / num_frames as f32;

                // Build the gradient string
                let mut result = String::from("\r");
                for (i, ch) in chars.iter().enumerate() {
                    if *ch == ' ' {
                        result.push(' ');
                    } else {
                        let base_t = if len > 1 { i as f32 / (len - 1) as f32 } else { 0.0 };
                        let t = (base_t + offset) % 1.0;
                        let (r, g, b) = interpolate_color(GRADIENT_START, GRADIENT_END, t);
                        result.push_str(&format!("\x1b[38;2;{};{};{}m{}", r, g, b, ch));
                    }
                }
                result.push_str("\x1b[0m");

                print!("{}", result);
                let _ = io::stdout().flush();

                std::thread::sleep(std::time::Duration::from_millis(80));
            }
        });

        Self {
            message,
            running,
            handle: Some(handle),
        }
    }

    pub fn finish(&mut self) {
        self.running.store(false, std::sync::atomic::Ordering::Relaxed);
        if let Some(handle) = self.handle.take() {
            let _ = handle.join();
        }
        // Clear the line
        print!("\r{}\r", " ".repeat(self.message.len() + 10));
        let _ = io::stdout().flush();
    }
}

impl Drop for GradientSpinner {
    fn drop(&mut self) {
        if self.running.load(std::sync::atomic::Ordering::Relaxed) {
            self.finish();
        }
    }
}

pub fn create_spinner(message: &str) -> GradientSpinner {
    GradientSpinner::new(message)
}

pub fn create_progress_bar(total: u64, message: &str) -> ProgressBar {
    let pb = ProgressBar::new(total);
    pb.set_style(
        ProgressStyle::default_bar()
            .template("{msg} [{bar:40.cyan/blue}] {pos}/{len} ({percent}%)")
            .unwrap()
            .progress_chars("█▓▒░ "),
    );
    pb.set_message(message.to_string());
    pb
}

pub fn prompt_confirmation(message: &str) -> io::Result<bool> {
    print!("{} [y/N]: ", gradient_start(message));
    io::stdout().flush()?;

    let mut input = String::new();
    io::stdin().read_line(&mut input)?;

    Ok(input.trim().eq_ignore_ascii_case("y") || input.trim().eq_ignore_ascii_case("yes"))
}

pub fn print_disposition(profile: &BankProfileResponse) {
    print_section_header(&format!("Disposition: {}", profile.bank_id));

    // Print name
    println!("{} {}", dim("Name:"), gradient_start(&profile.name));
    println!();

    // Print mission if available
    if !profile.mission.is_empty() {
        println!("{}", gradient_mid("Mission:"));
        for line in profile.mission.lines() {
            println!("{}", line);
        }
        println!();
    }

    // Print disposition traits
    println!("{}", gradient_text("─── Disposition Traits ───"));
    println!();

    // New 3-trait disposition system (values 1-5)
    let traits: [(_, i64, f32, _); 3] = [
        ("Skepticism", profile.disposition.skepticism.get() as i64, 0.0, "1=trusting, 5=skeptical"),
        ("Literalism", profile.disposition.literalism.get() as i64, 0.5, "1=flexible, 5=literal"),
        ("Empathy", profile.disposition.empathy.get() as i64, 1.0, "1=detached, 5=empathetic"),
    ];

    for (name, value, t, desc) in &traits {
        // Scale 1-5 to bar visualization (each point = 8 chars, total 40)
        let bar_length = 40;
        let filled = ((*value - 1) * 10) as usize; // 1->0, 2->10, 3->20, 4->30, 5->40
        let empty = bar_length - filled;

        let bar = format!("{}{}", "█".repeat(filled), "░".repeat(empty));

        println!("  {:<12} [{}] {}/5",
            name,
            gradient(&bar, *t),
            value
        );
        println!("    {}", dim(desc));
    }

    println!();
}
