//! Output formatting utilities for steme-pharma CLI.

use comfy_table::{presets::UTF8_FULL, Cell, Color, ContentArrangement, Table};
use stemedb_ontology::dto::{LayeredResponse, ObjectValueDto, QueryResponse, SkepticResponse};

/// Format a SkepticResponse as a table.
pub fn format_skeptic_table(response: &SkepticResponse) -> String {
    let mut output = String::new();

    // Header
    output.push_str(&format!("{} / {}\n", response.subject, response.predicate));
    output.push_str(&format!(
        "Status: {:?} | Conflict: {:.2} | Candidates: {}\n\n",
        response.status, response.conflict_score, response.candidates_count
    ));

    // Claims table
    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["#", "Value", "Weight", "Count", "Source"]);

    for (i, claim) in response.claims.iter().enumerate() {
        let value_str = format_object_value(&claim.value);
        let weight_str = format!("{:.1}%", claim.weight_share * 100.0);
        let count_str = claim.assertion_count.to_string();
        let source_short = &claim.representative_hash[..8.min(claim.representative_hash.len())];

        table.add_row(vec![
            Cell::new(i + 1),
            Cell::new(value_str),
            Cell::new(weight_str),
            Cell::new(count_str),
            Cell::new(source_short),
        ]);
    }

    output.push_str(&table.to_string());
    output
}

/// Format a LayeredResponse as a table.
pub fn format_layered_table(response: &LayeredResponse) -> String {
    let mut output = String::new();

    // Header
    output.push_str(&format!("{} / {}\n", response.subject, response.predicate));
    output.push_str(&format!(
        "Cross-tier Conflict: {:.2} | Total: {}\n\n",
        response.overall_conflict_score, response.total_candidates
    ));

    // Tiers table
    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["Tier", "Source", "Winner", "Conflict", "Count"]);

    for tier in &response.tiers {
        let winner_str = tier
            .winner
            .as_ref()
            .map(|w| format_object_value(&w.object))
            .unwrap_or_else(|| "-".to_string());
        let source_str = format!("{:?}", tier.source_class);

        let mut tier_cell = Cell::new(format!("T{}", tier.tier));
        // Color by tier (regulatory=green, anecdotal=yellow)
        tier_cell = match tier.tier {
            0 => tier_cell.fg(Color::Green),
            1..=2 => tier_cell.fg(Color::Cyan),
            3..=4 => tier_cell.fg(Color::Yellow),
            _ => tier_cell.fg(Color::Red),
        };

        table.add_row(vec![
            tier_cell,
            Cell::new(source_str),
            Cell::new(winner_str),
            Cell::new(format!("{:.2}", tier.conflict_score)),
            Cell::new(tier.candidates_count.to_string()),
        ]);
    }

    output.push_str(&table.to_string());

    // Overall winner
    if let Some(ref winner) = response.overall_winner {
        output.push_str(&format!(
            "\nOverall Winner: {} (from {:?})\n",
            format_object_value(&winner.object),
            winner.source_class
        ));
    }

    output
}

/// Format a QueryResponse as a table.
pub fn format_query_table(response: &QueryResponse, subject: &str, predicate: &str) -> String {
    let mut output = String::new();

    // Header
    output.push_str(&format!("{} / {}\n", subject, predicate));
    output.push_str(&format!(
        "Results: {} | Conflict: {}\n\n",
        response.total_count,
        response.conflict_score.map(|c| format!("{:.2}", c)).unwrap_or_else(|| "-".to_string())
    ));

    // Assertions table
    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["#", "Value", "Source", "Confidence", "Hash"]);

    for (i, assertion) in response.assertions.iter().enumerate() {
        let value_str = format_object_value(&assertion.object);
        let source_str = format!("{:?}", assertion.source_class);
        let hash_short = &assertion.hash[..8.min(assertion.hash.len())];

        table.add_row(vec![
            Cell::new(i + 1),
            Cell::new(value_str),
            Cell::new(source_str),
            Cell::new(format!("{:.2}", assertion.confidence)),
            Cell::new(hash_short),
        ]);
    }

    output.push_str(&table.to_string());
    output
}

/// Format comparison between two skeptic responses.
pub fn format_compare_table(
    subj1: &str,
    subj2: &str,
    resp1: &SkepticResponse,
    resp2: &SkepticResponse,
) -> String {
    let mut output = String::new();

    output.push_str(&format!("Comparing: {} vs {} on {}\n\n", subj1, subj2, resp1.predicate));

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["Metric", subj1, subj2, "Delta"]);

    // Winner values
    let winner1 = resp1
        .claims
        .first()
        .map(|c| format_object_value(&c.value))
        .unwrap_or_else(|| "-".to_string());
    let winner2 = resp2
        .claims
        .first()
        .map(|c| format_object_value(&c.value))
        .unwrap_or_else(|| "-".to_string());

    table.add_row(vec![
        Cell::new("Winner"),
        Cell::new(&winner1),
        Cell::new(&winner2),
        Cell::new("-"),
    ]);

    // Conflict scores
    let delta_conflict = resp2.conflict_score - resp1.conflict_score;
    table.add_row(vec![
        Cell::new("Conflict"),
        Cell::new(format!("{:.2}", resp1.conflict_score)),
        Cell::new(format!("{:.2}", resp2.conflict_score)),
        Cell::new(format!("{:+.2}", delta_conflict)),
    ]);

    // Candidate counts
    let delta_candidates = resp2.candidates_count as i64 - resp1.candidates_count as i64;
    table.add_row(vec![
        Cell::new("Candidates"),
        Cell::new(resp1.candidates_count.to_string()),
        Cell::new(resp2.candidates_count.to_string()),
        Cell::new(format!("{:+}", delta_candidates)),
    ]);

    // Status
    table.add_row(vec![
        Cell::new("Status"),
        Cell::new(format!("{:?}", resp1.status)),
        Cell::new(format!("{:?}", resp2.status)),
        Cell::new("-"),
    ]);

    output.push_str(&table.to_string());
    output
}

/// Format a list of predicates.
pub fn format_explore_table(subject: &str, predicates: &[String]) -> String {
    let mut output = String::new();

    output.push_str(&format!("Predicates for: {}\n", subject));
    output.push_str(&format!("Found: {} predicates\n\n", predicates.len()));

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["#", "Predicate"]);

    for (i, pred) in predicates.iter().enumerate() {
        table.add_row(vec![Cell::new(i + 1), Cell::new(pred)]);
    }

    output.push_str(&table.to_string());
    output
}

/// Format an ObjectValueDto for display.
pub fn format_object_value(value: &ObjectValueDto) -> String {
    match value {
        ObjectValueDto::Text(s) => {
            if s.len() > 30 {
                format!("\"{}...\"", &s[..27])
            } else {
                format!("\"{}\"", s)
            }
        }
        ObjectValueDto::Number(n) => format!("{}", n),
        ObjectValueDto::Boolean(b) => format!("{}", b),
        ObjectValueDto::Reference(r) => format!("@{}", r),
    }
}
