"""Flat channel grid component.

A compact, sortable data table of all channels with:
- Wildcard filter bar (ISO code pattern matching)
- Facet dropdowns (body region, measurement type, direction)
- Checkbox selection with color dots matching plot traces
- Columns: selected, ISO code, description, unit, min, max

Channels are displayed in file load order (preserving the user's
original channel ordering). Filtering reduces the visible set
without reordering.
"""

from __future__ import annotations

import fnmatch
import re
from typing import Any

import dash_bootstrap_components as dbc
from dash import dash_table, dcc, html

from impakt.channel.lookup import DIRECTIONS, MAIN_LOCATIONS, MEASUREMENTS
from impakt.plot.engine import DEFAULT_COLORS
from impakt.web.state import AppState


def _build_channel_rows(app_state: AppState) -> list[dict[str, Any]]:
    """Build flat row data for the channel grid.

    Each row is a dict with fields for the DataTable. Channels appear
    in their original file order.
    """
    rows: list[dict[str, Any]] = []
    multi_test = len(app_state.tests) > 1

    for loaded in app_state.tests:
        test_id = loaded.test_id
        ch_num = 0
        for ch in loaded.data:
            ch_num += 1
            code = ch.code

            # Build human-readable description
            if code.is_valid:
                desc = code.short_label
                body = MAIN_LOCATIONS.get(code.main_location, code.main_location)
                meas = code.measurement
                meas_label = MEASUREMENTS.get(meas, (meas,))[0] if meas in MEASUREMENTS else meas
                direction = code.direction
                occupant = code.test_object
            else:
                desc = ch.name
                body = ""
                meas = ""
                meas_label = ""
                direction = ""
                occupant = ""

            rows.append(
                {
                    "key": f"{test_id}::{ch.name}",
                    "ch_num": ch_num,
                    "test_id": test_id if multi_test else "",
                    "iso_code": ch.name,
                    "description": desc,
                    "unit": ch.unit,
                    "min": f"{float(ch.data.min()):.2f}",
                    "max": f"{float(ch.data.max()):.2f}",
                    "rate": f"{ch.sample_rate:.0f}",
                    # Facet fields (for filtering, not necessarily displayed)
                    "body": body,
                    "meas": meas_label,
                    "direction": direction,
                    "occupant": occupant,
                }
            )

    return rows


def _extract_facet_options(rows: list[dict[str, Any]], field: str) -> list[dict[str, str]]:
    """Extract unique values for a facet dropdown."""
    values = sorted({r[field] for r in rows if r[field]})
    return [{"label": v, "value": v} for v in values]


def build_channel_grid(app_state: AppState) -> dbc.Card:
    """Build the channel grid panel."""

    if app_state.is_empty:
        return dbc.Card(
            [
                dbc.CardHeader(
                    [
                        html.Span("Channels", className="fw-bold"),
                    ],
                    className="py-2",
                ),
                dbc.CardBody(
                    html.Div("No test loaded", className="text-muted small text-center py-3"),
                ),
            ]
        )

    rows = _build_channel_rows(app_state)
    multi_test = len(app_state.tests) > 1

    # Facet options
    body_options = _extract_facet_options(rows, "body")
    meas_options = _extract_facet_options(rows, "meas")
    dir_options = _extract_facet_options(rows, "direction")

    # Columns for the DataTable
    columns = [
        {"name": "#", "id": "ch_num", "type": "numeric"},
        {"name": "ISO Code", "id": "iso_code"},
        {"name": "Description", "id": "description"},
        {"name": "Unit", "id": "unit"},
        {"name": "Min", "id": "min", "type": "numeric"},
        {"name": "Max", "id": "max", "type": "numeric"},
    ]
    if multi_test:
        columns.insert(1, {"name": "Test", "id": "test_id"})

    return dbc.Card(
        [
            dbc.CardHeader(
                [
                    html.Span("Channels", className="fw-bold"),
                    html.Span(f" ({len(rows)})", style={"fontSize": "11px", "color": "#999"}),
                ],
                className="py-2",
            ),
            dbc.CardBody(
                [
                    # Wildcard filter bar
                    dbc.InputGroup(
                        [
                            dbc.Input(
                                id="channel-filter-input",
                                placeholder="Filter: *HEAD*AC*, 11*FO*Z*, ...",
                                type="text",
                                size="sm",
                                debounce=True,
                                style={"fontSize": "12px", "fontFamily": "monospace"},
                            ),
                            dbc.Button(
                                "Clear",
                                id="channel-filter-clear",
                                color="outline-secondary",
                                size="sm",
                                style={"fontSize": "11px"},
                            ),
                        ],
                        size="sm",
                        className="mb-2",
                    ),
                    # Facet dropdowns
                    dbc.Row(
                        [
                            dbc.Col(
                                [
                                    dbc.Select(
                                        id="facet-body",
                                        options=[{"label": "Body region...", "value": ""}]
                                        + body_options,
                                        value="",
                                        size="sm",
                                        style={"fontSize": "11px"},
                                    ),
                                ],
                                width=4,
                            ),
                            dbc.Col(
                                [
                                    dbc.Select(
                                        id="facet-meas",
                                        options=[{"label": "Measurement...", "value": ""}]
                                        + meas_options,
                                        value="",
                                        size="sm",
                                        style={"fontSize": "11px"},
                                    ),
                                ],
                                width=4,
                            ),
                            dbc.Col(
                                [
                                    dbc.Select(
                                        id="facet-direction",
                                        options=[{"label": "Dir...", "value": ""}] + dir_options,
                                        value="",
                                        size="sm",
                                        style={"fontSize": "11px"},
                                    ),
                                ],
                                width=4,
                            ),
                        ],
                        className="mb-2",
                    ),
                    # Selected channels badge row
                    html.Div(id="selected-channels-badges", className="mb-2"),
                    # Channel DataTable
                    html.Div(
                        dash_table.DataTable(
                            id="channel-grid",
                            columns=columns,
                            data=rows,
                            row_selectable="multi",
                            selected_rows=[],
                            sort_action="native",
                            sort_mode="multi",
                            page_action="none",
                            style_table={
                                "overflowY": "auto",
                                "maxHeight": "400px",
                            },
                            style_header={
                                "backgroundColor": "#f8f9fa",
                                "fontWeight": "bold",
                                "fontSize": "11px",
                                "padding": "4px 8px",
                                "borderBottom": "2px solid #dee2e6",
                            },
                            style_cell={
                                "fontSize": "11px",
                                "fontFamily": "'SF Mono', 'Menlo', 'Monaco', monospace",
                                "padding": "3px 8px",
                                "textAlign": "left",
                                "border": "none",
                                "borderBottom": "1px solid #f0f0f0",
                                "overflow": "hidden",
                                "textOverflow": "ellipsis",
                                "whiteSpace": "nowrap",
                            },
                            style_cell_conditional=[
                                {
                                    "if": {"column_id": "ch_num"},
                                    "width": "36px",
                                    "textAlign": "right",
                                    "color": "#999",
                                },
                                {
                                    "if": {"column_id": "iso_code"},
                                    "minWidth": "140px",
                                },
                                {
                                    "if": {"column_id": "description"},
                                    "minWidth": "80px",
                                    "fontFamily": "inherit",
                                },
                                {
                                    "if": {"column_id": "unit"},
                                    "width": "50px",
                                    "textAlign": "center",
                                },
                                {"if": {"column_id": "min"}, "width": "65px", "textAlign": "right"},
                                {"if": {"column_id": "max"}, "width": "65px", "textAlign": "right"},
                                {"if": {"column_id": "test_id"}, "width": "60px"},
                            ],
                            style_data_conditional=[],
                            style_as_list_view=True,
                            fixed_rows={"headers": True},
                        ),
                    ),
                    # Hidden store for full row data (used by callbacks to resolve keys)
                    dcc.Store(id="channel-grid-all-rows", data=rows),
                ],
                className="py-2",
            ),
        ]
    )


def build_selected_channels_badges(
    selected_keys: list[str],
    app_state: AppState,
) -> list:
    """Build badge pills showing currently selected channels."""
    if not selected_keys:
        return [
            html.Span("No channels selected", className="text-muted", style={"fontSize": "11px"})
        ]

    badges = []
    for i, key in enumerate(selected_keys[:15]):
        # Resolve label
        if "::" in key:
            test_id, ch_name = key.split("::", 1)
            ch = app_state.get_channel(test_id, ch_name)
        else:
            ch = None
            ch_name = key

        if ch and ch.code.is_valid:
            label = ch.code.short_label
        else:
            label = ch_name

        if len(app_state.tests) > 1 and "::" in key:
            label = f"{key.split('::')[0]}: {label}"

        # Color dot matching plot trace
        color = DEFAULT_COLORS[i % len(DEFAULT_COLORS)]

        badges.append(
            html.Span(
                [
                    html.Span("\u25cf ", style={"color": color, "fontSize": "10px"}),
                    html.Span(label, style={"fontSize": "10px"}),
                ],
                className="badge bg-light text-dark border me-1 mb-1",
                style={"fontWeight": "normal"},
            ),
        )

    if len(selected_keys) > 15:
        badges.append(
            html.Span(
                f"+{len(selected_keys) - 15} more",
                className="text-muted",
                style={"fontSize": "10px"},
            )
        )

    return badges


def filter_rows(
    all_rows: list[dict[str, Any]],
    pattern: str = "",
    body: str = "",
    meas: str = "",
    direction: str = "",
) -> list[dict[str, Any]]:
    """Filter channel rows by wildcard pattern and facets.

    Args:
        all_rows: Full list of channel rows.
        pattern: Wildcard pattern matched against ISO code (e.g., '*HEAD*AC*').
        body: Body region facet filter.
        meas: Measurement type facet filter.
        direction: Direction facet filter.

    Returns:
        Filtered list of rows.
    """
    result = all_rows

    if pattern:
        # Normalize: if the user didn't use wildcards, wrap in *...*
        p = pattern.strip().upper()
        if "*" not in p and "?" not in p:
            p = f"*{p}*"
        result = [r for r in result if fnmatch.fnmatch(r["iso_code"].upper(), p)]

    if body:
        result = [r for r in result if r["body"] == body]

    if meas:
        result = [r for r in result if r["meas"] == meas]

    if direction:
        result = [r for r in result if r["direction"] == direction]

    return result
