use crate::app::{App, InputMode, ListItem, TreeItem, SortMode};
use crate::config::parse_color;
use crate::zfs::format_size;
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Clear, List, ListItem as RatatuiListItem, Paragraph},
    Frame,
};

// Helper to get colors from app config
fn get_colors(app: &App) -> (Color, Color, Color, Color, Color, Color, Color, Color, Color, Color) {
    let c = &app.config.colors;
    (
        parse_color(&c.background),
        parse_color(&c.foreground),
        parse_color(&c.header),
        parse_color(&c.selected_bg),
        parse_color(&c.selected_fg),
        parse_color(&c.marked),
        parse_color(&c.snapshot),
        parse_color(&c.size),
        parse_color(&c.tree),
        parse_color(&c.search_match),
    )
}

pub fn draw(f: &mut Frame, app: &mut App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(1), // Title
            Constraint::Min(10),   // Main content
            Constraint::Length(1), // Status bar
            Constraint::Length(1), // Function keys / Input
        ])
        .split(f.area());

    draw_title(f, chunks[0], app);
    draw_main(f, chunks[1], app);
    draw_status(f, chunks[2], app);

    if app.input_mode != InputMode::Normal {
        draw_input(f, chunks[3], app);
    } else {
        draw_function_keys(f, chunks[3], app);
    }

    if app.show_help {
        draw_help(f, app);
    }
}

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

fn draw_title(f: &mut Frame, area: Rect, app: &App) {
    let (bg, _fg, header, _, _, _, _, _, _, _) = get_colors(app);

    let mut title = format!(" zfsdu v{} ", VERSION);

    // Show sort mode
    title.push_str(&format!("[Sort: {}] ", app.sort_mode.label()));

    if !app.filter_query.is_empty() {
        title.push_str(&format!("[Filter: {}] ", app.filter_query));
    }

    if app.dry_run_mode {
        title.push_str("[DRY-RUN] ");
    }

    if app.auto_refresh {
        title.push_str("[AUTO] ");
    }

    let title_bar = Paragraph::new(title).style(
        Style::default()
            .bg(bg)
            .fg(header)
            .add_modifier(Modifier::BOLD),
    );
    f.render_widget(title_bar, area);
}

fn draw_main(f: &mut Frame, area: Rect, app: &mut App) {
    let (bg, fg, header_color, _, _, _, _, _, _, _) = get_colors(app);

    let block = Block::default()
        .borders(Borders::ALL)
        .border_style(Style::default().fg(fg).bg(bg))
        .style(Style::default().bg(bg));

    let inner = block.inner(area);
    f.render_widget(block, area);

    // Header - dynamic width with date column
    let header_area = Rect {
        x: inner.x,
        y: inner.y,
        width: inner.width,
        height: 1,
    };
    // Layout: name + date(12) + size(10) + bar(8) + spacing
    let header_name_width = (inner.width as usize).saturating_sub(34);
    let header = Line::from(vec![
        Span::styled(
            format!(" {:<width$}", "Name", width = header_name_width),
            Style::default()
                .fg(header_color)
                .add_modifier(Modifier::BOLD),
        ),
        Span::styled(
            format!("{:>12}", "Date"),
            Style::default()
                .fg(header_color)
                .add_modifier(Modifier::BOLD),
        ),
        Span::styled(
            format!("{:>10}", "Size"),
            Style::default()
                .fg(header_color)
                .add_modifier(Modifier::BOLD),
        ),
    ]);
    f.render_widget(
        Paragraph::new(header).style(Style::default().bg(bg)),
        header_area,
    );

    // Separator
    let sep_area = Rect {
        x: inner.x,
        y: inner.y + 1,
        width: inner.width,
        height: 1,
    };
    let separator = "─".repeat(inner.width as usize);
    f.render_widget(
        Paragraph::new(separator).style(Style::default().fg(fg).bg(bg)),
        sep_area,
    );

    // List area
    let list_area = Rect {
        x: inner.x,
        y: inner.y + 2,
        width: inner.width,
        height: inner.height.saturating_sub(2),
    };

    // Update page_size based on visible area
    app.page_size = list_area.height as usize;

    // Get visible items with scrolling
    let visible_items: Vec<(usize, &TreeItem)> = app.visible_items();
    let total_visible = visible_items.len();

    // Apply scroll offset
    let display_items: Vec<(usize, usize, &TreeItem)> = visible_items
        .into_iter()
        .enumerate()
        .skip(app.scroll_offset)
        .take(list_area.height as usize)
        .map(|(visible_idx, (real_idx, ti))| (visible_idx, real_idx, ti))
        .collect();

    let available_width = list_area.width as usize;
    let items: Vec<RatatuiListItem> = display_items
        .iter()
        .map(|(visible_idx, real_idx, tree_item)| {
            render_tree_item(app, *visible_idx, *real_idx, tree_item, available_width)
        })
        .collect();

    let list = List::new(items).style(Style::default().bg(bg));
    f.render_widget(list, list_area);

    // Draw scrollbar indicator if needed
    if total_visible > list_area.height as usize {
        draw_scrollbar(f, list_area, app.scroll_offset, total_visible, list_area.height as usize, bg);
    }
}

fn render_tree_item(app: &App, visible_index: usize, real_index: usize, tree_item: &TreeItem, available_width: usize) -> RatatuiListItem<'static> {
    let (bg, fg, _, selected_bg, selected_fg, marked_color, snapshot_color, size_color, tree_color, search_match_color) = get_colors(app);

    let is_selected = visible_index == app.selected;
    let is_marked = app.is_marked(tree_item.item.full_name());
    let is_search_match = app.is_search_match(real_index);

    // Build tree prefix (│, ├──, └──)
    let mut tree_prefix = String::new();

    for &parent_last in &tree_item.parent_is_last {
        if parent_last {
            tree_prefix.push_str("    ");
        } else {
            tree_prefix.push_str(" │  ");
        }
    }

    if tree_item.depth > 0 {
        if tree_item.is_last {
            tree_prefix.push_str(" └──");
        } else {
            tree_prefix.push_str(" ├──");
        }
    }

    // Item prefix (icon)
    let (icon, name_color) = match &tree_item.item {
        ListItem::Dataset(d) => {
            let has_children = d.has_children();
            let is_expanded = app.is_expanded(&d.full_name);
            let icon = if has_children {
                if is_expanded { "▾ " } else { "▸ " }
            } else {
                "  "
            };
            (icon, fg)
        }
        ListItem::Snapshot(_) => {
            let icon = if is_marked { "● " } else { "○ " };
            (icon, snapshot_color)
        }
    };

    // Calculate available width for name dynamically
    // Layout: prefix + icon(2) + name + date(12) + size(10) + space(1) + bar(8) + scrollbar(2)
    let prefix_len = tree_prefix.chars().count();
    let fixed_width = prefix_len + 2 + 12 + 10 + 1 + 8 + 2; // prefix + icon + date + size + space + bar + scrollbar
    let max_name_len = available_width.saturating_sub(fixed_width).max(20);
    let name = truncate_str(tree_item.item.name(), max_name_len);
    let name_padded = format!("{:<width$}", name, width = max_name_len);

    // Format date (show only date part, not time)
    let creation = tree_item.item.creation();
    let date_str = format_date(creation);

    let size = format!("{:>10}", format_size(tree_item.item.used()));
    let bar = create_size_bar(tree_item.item.used(), app.max_size());

    // Determine styles
    let tree_style = Style::default().fg(tree_color).bg(bg);

    let mut item_style = if is_marked {
        Style::default()
            .fg(marked_color)
            .bg(bg)
            .add_modifier(Modifier::BOLD)
    } else if is_search_match {
        Style::default()
            .fg(search_match_color)
            .bg(bg)
    } else {
        Style::default().fg(name_color).bg(bg)
    };

    let mut size_style = Style::default().fg(size_color).bg(bg);
    let mut date_style = Style::default().fg(Color::Gray).bg(bg);

    if is_selected {
        item_style = item_style.bg(selected_bg).fg(selected_fg);
        size_style = size_style.bg(selected_bg).fg(selected_fg);
        date_style = date_style.bg(selected_bg).fg(selected_fg);
    }

    let line = Line::from(vec![
        Span::styled(tree_prefix, if is_selected { item_style } else { tree_style }),
        Span::styled(icon.to_string(), item_style),
        Span::styled(name_padded, item_style),
        Span::styled(format!("{:>12}", date_str), date_style),
        Span::styled(size, size_style),
        Span::raw(" "),
        Span::styled(bar, size_style),
    ]);

    RatatuiListItem::new(line).style(Style::default().bg(bg))
}

fn draw_scrollbar(f: &mut Frame, area: Rect, offset: usize, total: usize, visible: usize, bg: Color) {
    if total == 0 || visible >= total {
        return;
    }

    let scrollbar_height = area.height as usize;
    let thumb_height = ((visible as f64 / total as f64) * scrollbar_height as f64).max(1.0) as usize;
    let thumb_pos = ((offset as f64 / (total - visible) as f64) * (scrollbar_height - thumb_height) as f64) as usize;

    for i in 0..scrollbar_height {
        let char = if i >= thumb_pos && i < thumb_pos + thumb_height {
            "█"
        } else {
            "░"
        };
        let scroll_area = Rect {
            x: area.x + area.width - 1,
            y: area.y + i as u16,
            width: 1,
            height: 1,
        };
        f.render_widget(
            Paragraph::new(char).style(Style::default().fg(Color::DarkGray).bg(bg)),
            scroll_area,
        );
    }
}

fn draw_status(f: &mut Frame, area: Rect, app: &App) {
    let marked_info = if app.marked.is_empty() {
        String::new()
    } else {
        format!(
            "{} marked ({})",
            app.marked.len(),
            format_size(app.total_marked_size())
        )
    };

    let msg = app.message.clone().unwrap_or_default();

    let status = if !msg.is_empty() {
        if marked_info.is_empty() {
            format!(" {}", msg)
        } else {
            format!(" {} │ {}", marked_info, msg)
        }
    } else if !marked_info.is_empty() {
        format!(" {}", marked_info)
    } else {
        format!(" {} items", app.visible_count())
    };

    let status_bar = Paragraph::new(status).style(
        Style::default().bg(Color::Cyan).fg(Color::Black),
    );
    f.render_widget(status_bar, area);
}

fn draw_input(f: &mut Frame, area: Rect, app: &App) {
    let (label, query) = match app.input_mode {
        InputMode::Search => ("Search: ", &app.search_query),
        InputMode::Filter => ("Filter: ", &app.filter_query),
        InputMode::Normal => return,
    };

    let input_line = format!("{}{}_", label, query);
    let input = Paragraph::new(input_line).style(
        Style::default().bg(Color::Black).fg(Color::White),
    );
    f.render_widget(input, area);
}

fn draw_function_keys(f: &mut Frame, area: Rect, app: &App) {
    let keys = vec![
        ("?", "Help"),
        ("s", "Sort"),
        ("D", if app.dry_run_mode { "DRY!" } else { "Dry" }),
        ("a", if app.auto_refresh { "Auto!" } else { "Auto" }),
        ("/", "Find"),
        ("f", "Filt"),
        ("d", "Del"),
        ("q", "Quit"),
    ];

    let spans: Vec<Span> = keys
        .iter()
        .flat_map(|(key, desc)| {
            vec![
                Span::styled(
                    format!("{}", key),
                    Style::default().bg(Color::Black).fg(Color::White),
                ),
                Span::styled(
                    format!("{} ", desc),
                    Style::default().bg(Color::Cyan).fg(Color::Black),
                ),
            ]
        })
        .collect();

    let line = Line::from(spans);
    let keys_bar = Paragraph::new(line).style(Style::default().bg(Color::Cyan));
    f.render_widget(keys_bar, area);
}

fn draw_help(f: &mut Frame, app: &App) {
    let (bg, fg, header, _, _, _, _, _, _, _) = get_colors(app);

    let area = centered_rect(70, 90, f.area());

    f.render_widget(Clear, area);

    let help_text = vec![
        "",
        "  Navigation:",
        "    ↑/k, ↓/j       Move up/down",
        "    Enter/→/l      Expand/collapse dataset",
        "    Home/End       First/last item",
        "    PgUp/PgDn      Page up/down",
        "    Mouse scroll   Scroll list",
        "    Left-click     Select item",
        "    Right-click    Toggle mark",
        "",
        "  Tree:",
        "    e              Expand all",
        "    c              Collapse all",
        "",
        "  Selection (current scope):",
        "    Space          Mark/unmark snapshot",
        "    +              Select all in scope",
        "    -              Unselect all in scope",
        "    *              Invert selection in scope",
        "",
        "  Search & Filter:",
        "    /              Search (n=next, N=prev)",
        "    f              Filter visible items",
        "    Esc            Clear filter",
        "",
        "  Sort & View:",
        "    s              Toggle sort: Size/Date/Name",
        "    D              Toggle dry-run mode",
        "    a              Toggle auto-refresh (30s)",
        "",
        "  Actions:",
        "    d / F8         Delete marked snapshots",
        "    r / F5         Refresh",
        "    ? / F1         This help",
        "    q / F10        Quit",
        "",
        "  Config: ~/.config/zfsdu/config.toml",
        "",
        "  (c) Florian Schermer - DATAZONE GmbH",
        "  https://datazone.de",
        "",
        "  Press any key to close",
    ];

    let help = Paragraph::new(help_text.join("\n"))
        .block(
            Block::default()
                .title(format!(" Help - zfsdu v{} ", VERSION))
                .borders(Borders::ALL)
                .border_style(Style::default().fg(header)),
        )
        .style(Style::default().bg(bg).fg(fg));

    f.render_widget(help, area);
}

fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage((100 - percent_y) / 2),
            Constraint::Percentage(percent_y),
            Constraint::Percentage((100 - percent_y) / 2),
        ])
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage((100 - percent_x) / 2),
            Constraint::Percentage(percent_x),
            Constraint::Percentage((100 - percent_x) / 2),
        ])
        .split(popup_layout[1])[1]
}

fn truncate_str(s: &str, max_len: usize) -> String {
    if s.chars().count() <= max_len {
        s.to_string()
    } else {
        let truncated: String = s.chars().take(max_len.saturating_sub(1)).collect();
        format!("{}…", truncated)
    }
}

fn create_size_bar(size: u64, max_size: u64) -> String {
    let bar_width = 8;
    if max_size == 0 {
        return " ".repeat(bar_width);
    }

    let filled = ((size as f64 / max_size as f64) * bar_width as f64).round() as usize;
    let filled = filled.min(bar_width);

    format!("{}{}", "█".repeat(filled), " ".repeat(bar_width - filled))
}

fn format_date(timestamp: &str) -> String {
    // ZFS returns timestamp as Unix epoch seconds
    if let Ok(ts) = timestamp.parse::<i64>() {
        use chrono::{DateTime, Utc};
        if let Some(dt) = DateTime::<Utc>::from_timestamp(ts, 0) {
            return dt.format("%Y-%m-%d").to_string();
        }
    }
    // Return original if parsing fails
    if timestamp.len() > 10 {
        timestamp[..10].to_string()
    } else {
        timestamp.to_string()
    }
}
