#![allow(dead_code)] // Phase 2: consumed by view/mod.rs screen dispatch

//! Issue list screen view.
//!
//! Composes the reusable [`EntityTable`] and [`FilterBar`] widgets
//! with issue-specific column definitions and [`TableRow`] implementation.

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

use crate::state::issue_list::{IssueListRow, IssueListState, SortField, SortOrder};
use crate::view::common::entity_table::{
    Align, ColumnDef, EntityTableState, TableColors, TableRow, render_entity_table,
};
use crate::view::common::filter_bar::{FilterBarColors, FilterBarState, render_filter_bar};

// ---------------------------------------------------------------------------
// TableRow implementation for IssueListRow
// ---------------------------------------------------------------------------

impl TableRow for IssueListRow {
    fn cells(&self, col_count: usize) -> Vec<String> {
        let mut cells = Vec::with_capacity(col_count);

        // Column order must match ISSUE_COLUMNS definition.
        // 0: IID
        cells.push(format!("#{}", self.iid));
        // 1: Title
        cells.push(self.title.clone());
        // 2: State
        cells.push(self.state.clone());
        // 3: Author
        cells.push(self.author.clone());
        // 4: Labels
        cells.push(self.labels.join(", "));
        // 5: Project
        cells.push(self.project_path.clone());

        cells.truncate(col_count);
        cells
    }
}

// ---------------------------------------------------------------------------
// Column definitions
// ---------------------------------------------------------------------------

/// Column definitions for the issue list table.
const ISSUE_COLUMNS: &[ColumnDef] = &[
    ColumnDef {
        name: "IID",
        min_width: 5,
        flex_weight: 0,
        priority: 0,
        align: Align::Right,
    },
    ColumnDef {
        name: "Title",
        min_width: 15,
        flex_weight: 4,
        priority: 0,
        align: Align::Left,
    },
    ColumnDef {
        name: "State",
        min_width: 7,
        flex_weight: 0,
        priority: 0,
        align: Align::Left,
    },
    ColumnDef {
        name: "Author",
        min_width: 8,
        flex_weight: 1,
        priority: 1,
        align: Align::Left,
    },
    ColumnDef {
        name: "Labels",
        min_width: 10,
        flex_weight: 2,
        priority: 2,
        align: Align::Left,
    },
    ColumnDef {
        name: "Project",
        min_width: 12,
        flex_weight: 1,
        priority: 3,
        align: Align::Left,
    },
];

// ---------------------------------------------------------------------------
// Colors
// ---------------------------------------------------------------------------

fn table_colors() -> TableColors {
    TableColors {
        header_fg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        header_bg: PackedRgba::rgb(0x34, 0x34, 0x31),
        row_fg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        row_alt_bg: PackedRgba::rgb(0x1C, 0x1B, 0x1A),
        selected_fg: PackedRgba::rgb(0x10, 0x0F, 0x0F),
        selected_bg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        sort_indicator: PackedRgba::rgb(0x87, 0x96, 0x6B),
        border: PackedRgba::rgb(0x40, 0x40, 0x3C),
    }
}

fn filter_colors() -> FilterBarColors {
    FilterBarColors {
        input_fg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        input_bg: PackedRgba::rgb(0x28, 0x28, 0x24),
        cursor_fg: PackedRgba::rgb(0x00, 0x00, 0x00),
        cursor_bg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        chip_fg: PackedRgba::rgb(0xCE, 0xCD, 0xC3),
        chip_bg: PackedRgba::rgb(0x40, 0x40, 0x3C),
        error_fg: PackedRgba::rgb(0xAF, 0x3A, 0x29),
        label_fg: PackedRgba::rgb(0x87, 0x87, 0x80),
    }
}

// ---------------------------------------------------------------------------
// Render
// ---------------------------------------------------------------------------

/// Render the full issue list screen.
///
/// Layout:
/// ```text
/// Row 0:     [Filter bar: / filter input_________]
/// Row 1:     [chip1] [chip2]   (if filter active)
/// Row 2:     ─────────────────────────────────────
/// Row 3..N:  IID  Title          State  Author  ...
///            ───────────────────────────────────────
///            #42  Fix login bug  open   alice   ...
///            #41  Add tests      open   bob     ...
/// Bottom:    Showing 42 of 128 issues
/// ```
pub fn render_issue_list(frame: &mut Frame<'_>, state: &IssueListState, area: Rect) {
    if area.height < 3 || area.width < 10 {
        return;
    }

    let mut y = area.y;
    let max_x = area.x.saturating_add(area.width);

    // -- Filter bar ---------------------------------------------------------
    let filter_area = Rect::new(area.x, y, area.width, 2.min(area.height));
    let fb_state = FilterBarState {
        input: state.filter_input.clone(),
        cursor: state.filter_input.len(),
        focused: state.filter_focused,
        tokens: crate::filter_dsl::parse_filter_tokens(&state.filter_input),
        unknown_fields: Vec::new(),
    };
    let filter_rows = render_filter_bar(frame, &fb_state, filter_area, &filter_colors());
    y = y.saturating_add(filter_rows);

    // -- Status line (total count) ------------------------------------------
    let remaining_height = area.height.saturating_sub(y - area.y);
    if remaining_height < 2 {
        return;
    }

    // Reserve bottom row for status.
    let table_height = remaining_height.saturating_sub(1);
    let status_y = y.saturating_add(table_height);

    // -- Entity table -------------------------------------------------------
    let sort_col = match state.sort_field {
        SortField::UpdatedAt => 0, // Map to IID column (closest visual proxy)
        SortField::Iid => 0,
        SortField::Title => 1,
        SortField::State => 2,
        SortField::Author => 3,
    };

    let mut table_state = EntityTableState {
        selected: state.selected_index,
        scroll_offset: state.scroll_offset,
        sort_column: sort_col,
        sort_ascending: matches!(state.sort_order, SortOrder::Asc),
    };

    let table_area = Rect::new(area.x, y, area.width, table_height);
    render_entity_table(
        frame,
        &state.rows,
        ISSUE_COLUMNS,
        &mut table_state,
        table_area,
        &table_colors(),
    );

    // -- Bottom status ------------------------------------------------------
    if status_y < area.y.saturating_add(area.height) {
        render_status_line(frame, state, area.x, status_y, max_x);
    }
}

/// Render the bottom status line showing row count and pagination info.
fn render_status_line(frame: &mut Frame<'_>, state: &IssueListState, x: u16, y: u16, max_x: u16) {
    let muted = Cell {
        fg: PackedRgba::rgb(0x87, 0x87, 0x80),
        ..Cell::default()
    };

    let status = if state.rows.is_empty() {
        "No issues found".to_string()
    } else {
        let showing = state.rows.len();
        let total = state.total_count;
        if state.next_cursor.is_some() {
            format!("Showing {showing} of {total} issues (more available)")
        } else {
            format!("Showing {showing} of {total} issues")
        }
    };

    frame.print_text_clipped(x, y, &status, muted, max_x);
}

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

#[cfg(test)]
mod tests {
    use super::*;
    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_state(row_count: usize) -> IssueListState {
        let rows: Vec<IssueListRow> = (0..row_count)
            .map(|i| IssueListRow {
                project_path: "group/project".into(),
                iid: (i + 1) as i64,
                title: format!("Issue {}", i + 1),
                state: if i % 2 == 0 { "opened" } else { "closed" }.into(),
                author: "taylor".into(),
                labels: if i == 0 {
                    vec!["bug".into(), "critical".into()]
                } else {
                    vec![]
                },
                updated_at: 1_700_000_000_000 - (i as i64 * 60_000),
            })
            .collect();

        IssueListState {
            total_count: row_count as u64,
            rows,
            ..Default::default()
        }
    }

    #[test]
    fn test_render_issue_list_no_panic() {
        with_frame!(120, 30, |frame| {
            let state = sample_state(10);
            render_issue_list(&mut frame, &state, Rect::new(0, 0, 120, 30));
        });
    }

    #[test]
    fn test_render_issue_list_empty_no_panic() {
        with_frame!(80, 20, |frame| {
            let state = IssueListState::default();
            render_issue_list(&mut frame, &state, Rect::new(0, 0, 80, 20));
        });
    }

    #[test]
    fn test_render_issue_list_tiny_noop() {
        with_frame!(5, 2, |frame| {
            let state = sample_state(5);
            render_issue_list(&mut frame, &state, Rect::new(0, 0, 5, 2));
            // Should not panic with too-small area.
        });
    }

    #[test]
    fn test_render_issue_list_narrow_no_panic() {
        with_frame!(40, 15, |frame| {
            let state = sample_state(5);
            render_issue_list(&mut frame, &state, Rect::new(0, 0, 40, 15));
        });
    }

    #[test]
    fn test_render_issue_list_with_filter_no_panic() {
        with_frame!(100, 25, |frame| {
            let mut state = sample_state(5);
            state.filter_input = "state:opened".into();
            state.filter_focused = true;
            render_issue_list(&mut frame, &state, Rect::new(0, 0, 100, 25));
        });
    }

    #[test]
    fn test_issue_list_row_cells() {
        let row = IssueListRow {
            project_path: "group/proj".into(),
            iid: 42,
            title: "Fix bug".into(),
            state: "opened".into(),
            author: "alice".into(),
            labels: vec!["bug".into(), "urgent".into()],
            updated_at: 1_700_000_000_000,
        };

        let cells = row.cells(6);
        assert_eq!(cells[0], "#42");
        assert_eq!(cells[1], "Fix bug");
        assert_eq!(cells[2], "opened");
        assert_eq!(cells[3], "alice");
        assert_eq!(cells[4], "bug, urgent");
        assert_eq!(cells[5], "group/proj");
    }

    #[test]
    fn test_issue_list_row_cells_truncated() {
        let row = IssueListRow {
            project_path: "g/p".into(),
            iid: 1,
            title: "t".into(),
            state: "opened".into(),
            author: "a".into(),
            labels: vec![],
            updated_at: 0,
        };

        // Request fewer columns than available.
        let cells = row.cells(3);
        assert_eq!(cells.len(), 3);
    }

    #[test]
    fn test_column_count() {
        assert_eq!(ISSUE_COLUMNS.len(), 6);
    }
}
