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(),
}
}