agent-supplements-rec 0.1.0

Curated supplement recommendation engine for longevity biomarker optimization
use comfy_table::{modifiers::UTF8_ROUND_CORNERS, presets::UTF8_FULL, Table, ContentArrangement};
use owo_colors::OwoColorize;

use crate::types::{Brand, Interaction, Product, RecommendOutput};

pub fn render_product_list(products: &[&Product]) {
    if products.is_empty() {
        eprintln!("{}", "No products found.".dimmed());
        return;
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .apply_modifier(UTF8_ROUND_CORNERS)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["ID", "Name", "Category", "Dosage", "Freq", "Targets"]);

    for p in products {
        let targets: Vec<String> = p.targets.iter().map(|t| t.biomarker.clone()).collect();
        table.add_row(vec![
            &p.id,
            &p.name,
            &p.category,
            &format!("{} {}", p.dosage, p.unit),
            &p.frequency,
            &targets.join(", "),
        ]);
    }

    println!("{table}");
}

pub fn render_product_detail(product: &Product, brand_name: &str) {
    println!("{}", product.name.bold());
    println!("{}", "".repeat(60).dimmed());
    println!("  {} {}", "ID:".dimmed(), product.id);
    println!("  {} {}", "Brand:".dimmed(), brand_name);
    println!("  {} {}", "Category:".dimmed(), product.category);
    println!("  {} {}", "Form:".dimmed(), product.form);
    println!("  {} {} {}", "Dosage:".dimmed(), product.dosage, product.unit);
    println!("  {} {}", "Frequency:".dimmed(), product.frequency);

    if !product.description.is_empty() {
        println!("  {} {}", "Description:".dimmed(), product.description);
    }
    if !product.notes.is_empty() {
        println!("  {} {}", "Notes:".dimmed(), product.notes);
    }

    if !product.targets.is_empty() {
        println!("\n  {}", "Biomarker Targets:".bold());
        for t in &product.targets {
            let arrow = if t.direction == "increase" { "" } else { "" };
            println!(
                "    {} {} {} [{}]",
                arrow, t.biomarker, t.evidence.dimmed(), t.mechanism.dimmed()
            );
        }
    }

    if !product.contraindications.is_empty() {
        println!("\n  {}", "Contraindications:".bold().red());
        for c in &product.contraindications {
            println!("{c}");
        }
    }
}

pub fn render_brands(brands: &[&Brand]) {
    if brands.is_empty() {
        eprintln!("{}", "No brands found.".dimmed());
        return;
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .apply_modifier(UTF8_ROUND_CORNERS)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["ID", "Name", "Country", "Website", "Verified"]);

    for b in brands {
        let verified = if b.verified {
            "yes".to_string()
        } else {
            "no".to_string()
        };
        table.add_row(vec![
            &b.id,
            &b.name,
            &b.country,
            &b.website,
            &verified,
        ]);
    }

    println!("{table}");
}

pub fn render_interactions(interactions: &[&Interaction]) {
    if interactions.is_empty() {
        println!("{}", "No interactions found between the specified products.".green());
        return;
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .apply_modifier(UTF8_ROUND_CORNERS)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["Product A", "Product B", "Severity", "Description"]);

    for i in interactions {
        table.add_row(vec![
            &i.product_a,
            &i.product_b,
            &i.severity,
            &i.description,
        ]);
    }

    println!("{table}");
}

pub fn render_recommendations(output: &RecommendOutput) {
    if output.recommendations.is_empty() {
        println!("{}", "No supplement recommendations generated.".dimmed());
        return;
    }

    println!(
        "{} ({} total)",
        "Supplement Recommendations".bold(),
        output.total_recommendations
    );
    println!("{}", "".repeat(70).dimmed());

    for (i, rec) in output.recommendations.iter().enumerate() {
        let priority_label = match rec.priority.as_str() {
            "red_flag" => format!("{}", "RED FLAG".red().bold()),
            "pattern" => format!("{}", "PATTERN".yellow().bold()),
            "suboptimal" => format!("{}", "SUBOPTIMAL".cyan().bold()),
            other => other.to_string(),
        };

        println!(
            "\n  {}. {} [{}]",
            i + 1,
            rec.product.name.bold(),
            priority_label
        );
        println!("     {} {}", "Evidence:".dimmed(), rec.evidence_level);
        println!("     {} {}", "Dosage:".dimmed(), format!("{} {} {}", rec.product.dosage, rec.product.unit, rec.product.frequency));
        println!("     {} {}", "Rationale:".dimmed(), rec.rationale);

        if !rec.target_biomarkers.is_empty() {
            for tb in &rec.target_biomarkers {
                let arrow = if tb.expected_direction == "increase" {
                    ""
                } else {
                    ""
                };
                println!(
                    "       {} {} {} {} ({}: {})",
                    arrow,
                    tb.biomarker,
                    format!("{:.1}", tb.current_value).dimmed(),
                    tb.current_unit.dimmed(),
                    "status".dimmed(),
                    colorize_status(&tb.current_status),
                );
            }
        }
    }

    if !output.interaction_warnings.is_empty() {
        println!("\n{}", "Interaction Warnings:".bold().yellow());
        for w in &output.interaction_warnings {
            println!(
                "{} [{}]: {}",
                w.products.join(" + "),
                w.severity,
                w.description
            );
        }
    }
}

fn colorize_status(status: &str) -> String {
    match status {
        "optimal" => format!("{}", status.green()),
        "good" => format!("{}", status.green()),
        "attention" => format!("{}", status.yellow()),
        "marginal" => format!("{}", status.yellow()),
        "concern" => format!("{}", status.red()),
        _ => status.to_string(),
    }
}