//! Doctor screen view — health check results.
//!
//! Renders a vertical list of health checks with colored status
//! indicators (green PASS, yellow WARN, red FAIL).

use ftui::core::geometry::Rect;
use ftui::render::cell::{Cell, PackedRgba};
use ftui::render::drawing::Draw;
use ftui::render::frame::Frame;

use crate::state::doctor::{DoctorState, HealthStatus};

use super::{TEXT, TEXT_MUTED};

/// Pass green.
const PASS_FG: PackedRgba = PackedRgba::rgb(0x87, 0x9A, 0x39);
/// Warning yellow.
const WARN_FG: PackedRgba = PackedRgba::rgb(0xD0, 0xA2, 0x15);
/// Fail red.
const FAIL_FG: PackedRgba = PackedRgba::rgb(0xD1, 0x4D, 0x41);

// ---------------------------------------------------------------------------
// Public entry point
// ---------------------------------------------------------------------------

/// Render the doctor screen.
pub fn render_doctor(frame: &mut Frame<'_>, state: &DoctorState, area: Rect) {
    if area.width < 10 || area.height < 3 {
        return;
    }

    let max_x = area.right();

    if !state.loaded {
        // Not yet loaded — show centered prompt.
        let msg = "Loading health checks...";
        let x = area.x + area.width.saturating_sub(msg.len() as u16) / 2;
        let y = area.y + area.height / 2;
        frame.print_text_clipped(
            x,
            y,
            msg,
            Cell {
                fg: TEXT_MUTED,
                ..Cell::default()
            },
            max_x,
        );
        return;
    }

    // Title.
    let overall = state.overall_status();
    let title_fg = status_color(overall);
    let title = format!("Doctor — {}", overall.label());
    frame.print_text_clipped(
        area.x + 2,
        area.y + 1,
        &title,
        Cell {
            fg: title_fg,
            ..Cell::default()
        },
        max_x,
    );

    // Summary line.
    let pass_count = state.count_by_status(HealthStatus::Pass);
    let warn_count = state.count_by_status(HealthStatus::Warn);
    let fail_count = state.count_by_status(HealthStatus::Fail);
    let summary = format!(
        "{} passed, {} warnings, {} failed",
        pass_count, warn_count, fail_count
    );
    frame.print_text_clipped(
        area.x + 2,
        area.y + 2,
        &summary,
        Cell {
            fg: TEXT_MUTED,
            ..Cell::default()
        },
        max_x,
    );

    // Health check rows.
    let rows_start_y = area.y + 4;
    let name_width = 16u16;

    for (i, check) in state.checks.iter().enumerate() {
        let y = rows_start_y + i as u16;
        if y >= area.bottom().saturating_sub(2) {
            break;
        }

        // Status badge.
        let badge = format!("[{}]", check.status.label());
        let badge_fg = status_color(check.status);
        frame.print_text_clipped(
            area.x + 2,
            y,
            &badge,
            Cell {
                fg: badge_fg,
                ..Cell::default()
            },
            max_x,
        );

        // Check name.
        let name_x = area.x + 2 + 7; // "[PASS] " = 7 chars
        let name = format!("{:<width$}", check.name, width = name_width as usize);
        frame.print_text_clipped(
            name_x,
            y,
            &name,
            Cell {
                fg: TEXT,
                ..Cell::default()
            },
            max_x,
        );

        // Detail text.
        let detail_x = name_x + name_width;
        let max_detail = area.right().saturating_sub(detail_x + 1) as usize;
        let detail = if check.detail.len() > max_detail {
            format!(
                "{}...",
                &check.detail[..check
                    .detail
                    .floor_char_boundary(max_detail.saturating_sub(3))]
            )
        } else {
            check.detail.clone()
        };
        frame.print_text_clipped(
            detail_x,
            y,
            &detail,
            Cell {
                fg: TEXT_MUTED,
                ..Cell::default()
            },
            max_x,
        );
    }

    // Hint at bottom.
    let hint_y = area.bottom().saturating_sub(1);
    frame.print_text_clipped(
        area.x + 2,
        hint_y,
        "Esc: back  |  lore doctor (full check)",
        Cell {
            fg: TEXT_MUTED,
            ..Cell::default()
        },
        max_x,
    );
}

/// Map health status to a display color.
fn status_color(status: HealthStatus) -> PackedRgba {
    match status {
        HealthStatus::Pass => PASS_FG,
        HealthStatus::Warn => WARN_FG,
        HealthStatus::Fail => FAIL_FG,
    }
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::state::doctor::HealthCheck;
    use ftui::render::grapheme_pool::GraphemePool;

    macro_rules! with_frame {
        ($width:expr, $height:expr, |$frame:ident| $body:block) => {{
            let mut pool = GraphemePool::new();
            let mut $frame = Frame::new($width, $height, &mut pool);
            $body
        }};
    }

    fn sample_checks() -> Vec<HealthCheck> {
        vec![
            HealthCheck {
                name: "Config".into(),
                status: HealthStatus::Pass,
                detail: "/home/user/.config/lore/config.json".into(),
            },
            HealthCheck {
                name: "Database".into(),
                status: HealthStatus::Pass,
                detail: "schema v12".into(),
            },
            HealthCheck {
                name: "Projects".into(),
                status: HealthStatus::Warn,
                detail: "0 projects configured".into(),
            },
            HealthCheck {
                name: "FTS Index".into(),
                status: HealthStatus::Fail,
                detail: "No documents indexed".into(),
            },
        ]
    }

    #[test]
    fn test_render_not_loaded() {
        with_frame!(80, 24, |frame| {
            let state = DoctorState::default();
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
        });
    }

    #[test]
    fn test_render_with_checks() {
        with_frame!(80, 24, |frame| {
            let mut state = DoctorState::default();
            state.apply_checks(sample_checks());
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
        });
    }

    #[test]
    fn test_render_all_pass() {
        with_frame!(80, 24, |frame| {
            let mut state = DoctorState::default();
            state.apply_checks(vec![HealthCheck {
                name: "Config".into(),
                status: HealthStatus::Pass,
                detail: "ok".into(),
            }]);
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
        });
    }

    #[test]
    fn test_render_tiny_terminal() {
        with_frame!(8, 2, |frame| {
            let mut state = DoctorState::default();
            state.apply_checks(sample_checks());
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
            // Should not panic.
        });
    }

    #[test]
    fn test_render_narrow_terminal_truncates() {
        with_frame!(40, 20, |frame| {
            let mut state = DoctorState::default();
            state.apply_checks(vec![HealthCheck {
                name: "Database".into(),
                status: HealthStatus::Pass,
                detail: "This is a very long detail string that should be truncated".into(),
            }]);
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
        });
    }

    #[test]
    fn test_render_many_checks_clips() {
        with_frame!(80, 10, |frame| {
            let mut state = DoctorState::default();
            let mut checks = Vec::new();
            for i in 0..20 {
                checks.push(HealthCheck {
                    name: format!("Check {i}"),
                    status: HealthStatus::Pass,
                    detail: "ok".into(),
                });
            }
            state.apply_checks(checks);
            let area = frame.bounds();
            render_doctor(&mut frame, &state, area);
            // Should clip without panicking.
        });
    }
}
