agent-device-rec 0.1.0

Health device recommendation engine for longevity monitoring
use comfy_table::{presets::UTF8_FULL, Table, ContentArrangement, Cell, Color};

use crate::types::{
    Device, DeviceBrand, DeviceComparison, DeviceRecommendOutput,
};

pub fn render_device_list(devices: &[&Device], brands: &[DeviceBrand]) {
    if devices.is_empty() {
        eprintln!("No devices found.");
        return;
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(vec!["ID", "Name", "Brand", "Category", "Price", "Rx"]);

    for d in devices {
        let brand_name = brands
            .iter()
            .find(|b| b.id == d.brand_id)
            .map(|b| b.name.as_str())
            .unwrap_or(&d.brand_id);
        let price = d
            .price_usd
            .map(|p| format!("${p}"))
            .unwrap_or_else(|| d.price_range.clone());
        let rx = if d.prescription_required { "Yes" } else { "No" };

        table.add_row(vec![&d.id, &d.name, brand_name, &d.category, &price, rx]);
    }

    println!("{table}");
}

pub fn render_device_detail(device: &Device, brand_name: &str) {
    println!();
    println!("  {} ({})", device.name, device.id);
    println!("  Brand: {brand_name}");
    println!("  Category: {}", device.category);
    println!(
        "  Price: {}",
        device
            .price_usd
            .map(|p| format!("${p} ({})", device.price_range))
            .unwrap_or_else(|| device.price_range.clone())
    );
    println!(
        "  Prescription: {}",
        if device.prescription_required {
            "Required"
        } else {
            "No"
        }
    );
    println!(
        "  Connectivity: {}",
        device.connectivity.join(", ")
    );
    println!("  Platforms: {}", device.platforms.join(", "));
    println!("  Description: {}", device.description);

    if !device.tracks.is_empty() {
        println!();
        println!("  Tracked Metrics:");
        for t in &device.tracks {
            let note = if t.notes.is_empty() {
                String::new()
            } else {
                format!("{}", t.notes)
            };
            println!("    - {} [{}]{note}", t.metric, t.accuracy);
        }
    }

    if !device.use_cases.is_empty() {
        println!();
        println!("  Use Cases: {}", device.use_cases.join(", "));
    }

    if !device.integrations.is_empty() {
        println!(
            "  Integrations: {}",
            device.integrations.join(", ")
        );
    }

    if !device.notes.is_empty() {
        println!("  Notes: {}", device.notes);
    }

    println!();
}

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

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

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

    println!("{table}");
}

pub fn render_recommendations(output: &DeviceRecommendOutput) {
    if output.recommendations.is_empty() {
        eprintln!("No device recommendations for this assessment.");
        return;
    }

    println!();
    println!(
        "  Device Recommendations ({} found)",
        output.total_recommendations
    );
    println!();

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

    for (i, rec) in output.recommendations.iter().enumerate() {
        let priority_cell = match rec.priority.as_str() {
            "red_flag_monitor" => Cell::new(&rec.priority).fg(Color::Red),
            "pattern_monitor" => Cell::new(&rec.priority).fg(Color::Yellow),
            _ => Cell::new(&rec.priority).fg(Color::Cyan),
        };
        let price = rec
            .device
            .price_usd
            .map(|p| format!("${p}"))
            .unwrap_or_else(|| rec.device.price_range.clone());

        table.add_row(vec![
            Cell::new(i + 1),
            priority_cell,
            Cell::new(&rec.device.name),
            Cell::new(&rec.device.category),
            Cell::new(&price),
            Cell::new(&rec.rationale),
        ]);
    }

    println!("{table}");
}

pub fn render_comparison(comparison: &DeviceComparison) {
    if comparison.devices.is_empty() {
        eprintln!("No devices to compare.");
        return;
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::Dynamic);

    // Header: "Attribute" + device names
    let mut header = vec!["Attribute".to_string()];
    for d in &comparison.devices {
        header.push(d.name.clone());
    }
    table.set_header(header);

    for row in &comparison.comparison_matrix {
        let mut cells: Vec<String> = vec![row.attribute.clone()];
        cells.extend(row.values.clone());
        table.add_row(cells);
    }

    println!("{table}");
}