use crate::core::app_state::AppState;
use crate::core::types::{Container, ContainerState, HealthStatus, SortField, SortState};
use crate::ui::formatters::{format_bytes, format_bytes_per_sec, format_time_elapsed};
use crate::ui::render::UiStyles;
use ratatui::{
    Frame,
    layout::Constraint,
    style::{Color, Style},
    widgets::{Block, Borders, Cell, Row, Table},
};

const VERSION: &str = env!("CARGO_PKG_VERSION");

/// Renders the container list view
pub fn render_container_list(
    f: &mut Frame,
    area: ratatui::layout::Rect,
    app_state: &mut AppState,
    styles: &UiStyles,
    show_host_column: bool,
) {
    let width = area.width;

    // Determine if we should show progress bars based on terminal width
    let show_progress_bars = width >= 128;

    // Use pre-sorted list instead of sorting every frame
    let rows: Vec<Row> = app_state
        .sorted_container_keys
        .iter()
        .filter_map(|key| app_state.containers.get(key))
        .map(|c| create_container_row(c, styles, show_host_column, show_progress_bars))
        .collect();

    let header = create_header_row(styles, show_host_column, app_state.sort_state);
    let table = create_table(
        rows,
        header,
        app_state.sorted_container_keys.len(),
        styles,
        show_host_column,
        show_progress_bars,
    );

    f.render_stateful_widget(table, area, &mut app_state.table_state);
}

/// Creates a table row for a single container
fn create_container_row<'a>(
    container: &'a Container,
    styles: &UiStyles,
    show_host_column: bool,
    show_progress_bars: bool,
) -> Row<'a> {
    // Check if container is running
    let is_running = container.state == ContainerState::Running;

    // Only show stats for running containers
    let (cpu_bar, cpu_style) = if is_running {
        let display = if show_progress_bars {
            create_progress_bar(container.stats.cpu, 20)
        } else {
            format!("{:5.1}%", container.stats.cpu)
        };
        (display, get_percentage_style(container.stats.cpu, styles))
    } else {
        (String::new(), Style::default())
    };

    let (memory_bar, memory_style) = if is_running {
        let display = if show_progress_bars {
            create_memory_progress_bar(
                container.stats.memory,
                container.stats.memory_used_bytes,
                container.stats.memory_limit_bytes,
                20,
            )
        } else {
            format!("{:5.1}%", container.stats.memory)
        };
        (
            display,
            get_percentage_style(container.stats.memory, styles),
        )
    } else {
        (String::new(), Style::default())
    };

    let network_tx = if is_running {
        format_bytes_per_sec(container.stats.network_tx_bytes_per_sec)
    } else {
        String::new()
    };

    let network_rx = if is_running {
        format_bytes_per_sec(container.stats.network_rx_bytes_per_sec)
    } else {
        String::new()
    };

    // Format time elapsed since creation - show "N/A" for non-running containers
    let time_elapsed = if is_running {
        format_time_elapsed(container.created.as_ref())
    } else {
        "N/A".to_string()
    };

    // Get status icon and color (health takes priority over state)
    let (icon, icon_style) = get_status_icon(&container.state, &container.health, styles);

    let mut cells = vec![
        Cell::from(container.id.as_str()),
        Cell::from(icon).style(icon_style),
        Cell::from(container.name.as_str()),
    ];

    if show_host_column {
        cells.push(Cell::from(container.host_id.as_str()));
    }

    cells.extend(vec![
        Cell::from(cpu_bar).style(cpu_style),
        Cell::from(memory_bar).style(memory_style),
        Cell::from(network_tx),
        Cell::from(network_rx),
        Cell::from(time_elapsed),
    ]);

    Row::new(cells)
}

/// Creates a text-based progress bar with percentage
fn create_progress_bar(percentage: f64, width: usize) -> String {
    // Clamp the bar visual to 100%, but display the actual percentage value
    let bar_percentage = percentage.clamp(0.0, 100.0);
    let filled_width = ((bar_percentage / 100.0) * width as f64).round() as usize;
    let empty_width = width.saturating_sub(filled_width);

    let bar = format!("{}{}", "█".repeat(filled_width), "░".repeat(empty_width));

    format!("{} {:5.1}%", bar, percentage)
}

/// Creates a text-based progress bar with memory used/limit display
fn create_memory_progress_bar(percentage: f64, used: u64, limit: u64, width: usize) -> String {
    // Clamp the bar visual to 100%, but display the actual percentage value
    let bar_percentage = percentage.clamp(0.0, 100.0);
    let filled_width = ((bar_percentage / 100.0) * width as f64).round() as usize;
    let empty_width = width.saturating_sub(filled_width);

    let bar = format!("{}{}", "█".repeat(filled_width), "░".repeat(empty_width));

    format!("{} {}/{}", bar, format_bytes(used), format_bytes(limit))
}

/// Returns the status icon and color based on container health (if available) or state
fn get_status_icon(
    state: &ContainerState,
    health: &Option<HealthStatus>,
    styles: &UiStyles,
) -> (String, Style) {
    // Prioritize health status if container has health checks configured
    if let Some(health_status) = health {
        let icon = styles.icons.health(health_status).to_string();
        let style = match health_status {
            HealthStatus::Healthy => Style::default().fg(Color::Green),
            HealthStatus::Unhealthy => Style::default().fg(Color::Red),
            HealthStatus::Starting => Style::default().fg(Color::Yellow),
        };
        return (icon, style);
    }

    // Use state-based icon if no health check is configured
    let icon = styles.icons.state(state).to_string();
    let style = match state {
        ContainerState::Running => Style::default().fg(Color::Green),
        ContainerState::Paused => Style::default().fg(Color::Yellow),
        ContainerState::Restarting => Style::default().fg(Color::Yellow),
        ContainerState::Removing => Style::default().fg(Color::Yellow),
        ContainerState::Exited => Style::default().fg(Color::Red),
        ContainerState::Dead => Style::default().fg(Color::Red),
        ContainerState::Created => Style::default().fg(Color::Cyan),
        ContainerState::Unknown => Style::default().fg(Color::Gray),
    };
    (icon, style)
}

/// Returns the appropriate style based on percentage value
fn get_percentage_style(value: f64, styles: &UiStyles) -> Style {
    if value > 80.0 {
        styles.high
    } else if value > 50.0 {
        styles.medium
    } else {
        styles.low
    }
}

/// Creates the table header row
fn create_header_row(
    styles: &UiStyles,
    show_host_column: bool,
    sort_state: SortState,
) -> Row<'static> {
    let sort_symbol = sort_state.direction.symbol();
    let sort_field = sort_state.field;

    let mut headers = vec![
        "ID".to_string(),
        "".to_string(), // Status icon column (no header text)
        if sort_field == SortField::Name {
            format!("Name {}", sort_symbol)
        } else {
            "Name".to_string()
        },
    ];

    if show_host_column {
        headers.push("Host".to_string());
    }

    headers.extend(vec![
        if sort_field == SortField::Cpu {
            format!("CPU % {}", sort_symbol)
        } else {
            "CPU %".to_string()
        },
        if sort_field == SortField::Memory {
            format!("Memory % {}", sort_symbol)
        } else {
            "Memory %".to_string()
        },
        "Net TX".to_string(),
        "Net RX".to_string(),
        if sort_field == SortField::Uptime {
            format!("Created {}", sort_symbol)
        } else {
            "Created".to_string()
        },
    ]);

    Row::new(headers).style(styles.header).bottom_margin(1)
}

/// Creates the complete table widget
fn create_table<'a>(
    rows: Vec<Row<'a>>,
    header: Row<'static>,
    container_count: usize,
    styles: &UiStyles,
    show_host_column: bool,
    show_progress_bars: bool,
) -> Table<'a> {
    let mut constraints = vec![
        Constraint::Length(12), // Container ID
        Constraint::Length(1),  // Status icon
        Constraint::Min(8),     // Name (minimum 8, flexible)
    ];

    if show_host_column {
        constraints.push(Constraint::Length(20)); // Host
    }

    // Adjust column widths based on whether progress bars are shown
    let cpu_width = if show_progress_bars {
        28 // CPU progress bar (20 chars + " 100.0%")
    } else {
        7 // Just percentage (" 100.0%")
    };

    let mem_width = if show_progress_bars {
        33 // Memory progress bar (20 chars + " 999M/999M" + padding)
    } else {
        7 // Just percentage (" 100.0%")
    };

    constraints.extend(vec![
        Constraint::Length(cpu_width), // CPU
        Constraint::Length(mem_width), // Memory
        Constraint::Length(12),        // Network TX (1.23MB/s)
        Constraint::Length(12),        // Network RX (4.56MB/s)
        Constraint::Length(15),        // Created
    ]);

    Table::new(rows, constraints)
        .header(header)
        .block(
            Block::default()
                .borders(Borders::NONE)
                .padding(ratatui::widgets::Padding::proportional(1))
                .title(format!(
                    "dtop v{} - {} containers ('?' for help, 'q' to quit)",
                    VERSION, container_count
                ))
                .style(styles.border),
        )
        .row_highlight_style(styles.selected)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_memory_progress_bar_format() {
        let bar = create_memory_progress_bar(50.0, 512 * 1024 * 1024, 1024 * 1024 * 1024, 20);
        assert!(bar.contains("512M/1G"));
        assert!(bar.contains("██████████")); // 50% filled = 10 blocks
    }

    #[test]
    fn test_create_memory_progress_bar_zero() {
        let bar = create_memory_progress_bar(0.0, 0, 1024 * 1024 * 1024, 20);
        assert!(bar.contains("0B/1G"));
        assert!(bar.starts_with("░░░░░░░░░░░░░░░░░░░░")); // All empty
    }

    #[test]
    fn test_create_memory_progress_bar_full() {
        let bar = create_memory_progress_bar(100.0, 1024 * 1024 * 1024, 1024 * 1024 * 1024, 20);
        assert!(bar.contains("1G/1G"));
        assert!(bar.starts_with("████████████████████")); // All filled
    }

    #[test]
    fn test_create_memory_progress_bar_clamps_over_100() {
        // Bar visual should clamp at 100% even if percentage > 100
        let bar = create_memory_progress_bar(150.0, 1536 * 1024 * 1024, 1024 * 1024 * 1024, 20);
        assert!(bar.starts_with("████████████████████")); // Still fully filled
    }

    #[test]
    fn test_percentage_style_thresholds() {
        let styles = UiStyles::default();

        // Test low threshold (green)
        let low_style = get_percentage_style(30.0, &styles);
        assert_eq!(low_style.fg, Some(Color::Green));

        // Test medium threshold (yellow)
        let medium_style = get_percentage_style(65.0, &styles);
        assert_eq!(medium_style.fg, Some(Color::Yellow));

        // Test high threshold (red)
        let high_style = get_percentage_style(85.0, &styles);
        assert_eq!(high_style.fg, Some(Color::Red));

        // Test boundary cases
        assert_eq!(get_percentage_style(50.0, &styles).fg, Some(Color::Green));
        assert_eq!(get_percentage_style(50.1, &styles).fg, Some(Color::Yellow));
        assert_eq!(get_percentage_style(80.0, &styles).fg, Some(Color::Yellow));
        assert_eq!(get_percentage_style(80.1, &styles).fg, Some(Color::Red));
    }

    #[test]
    fn test_color_coding_boundaries() {
        let styles = UiStyles::default();

        // Test exact boundary values
        assert_eq!(
            get_percentage_style(0.0, &styles).fg,
            Some(Color::Green),
            "0% should be green"
        );
        assert_eq!(
            get_percentage_style(50.0, &styles).fg,
            Some(Color::Green),
            "50% should be green"
        );
        assert_eq!(
            get_percentage_style(50.1, &styles).fg,
            Some(Color::Yellow),
            "50.1% should be yellow"
        );
        assert_eq!(
            get_percentage_style(80.0, &styles).fg,
            Some(Color::Yellow),
            "80% should be yellow"
        );
        assert_eq!(
            get_percentage_style(80.1, &styles).fg,
            Some(Color::Red),
            "80.1% should be red"
        );
        assert_eq!(
            get_percentage_style(100.0, &styles).fg,
            Some(Color::Red),
            "100% should be red"
        );
    }
}
