"""Collapsible channel tree component.

Renders channels in a hierarchical accordion:
    Test Object > Body Region > Measurement Type > [channels]

Supports search filtering, select-all per group, and channel preview
(peak, unit, sample rate) on hover/expand.
"""

from __future__ import annotations

from typing import Any

import dash_bootstrap_components as dbc
from dash import dcc, html

from impakt.web.state import AppState


def _make_channel_item(ch_info: dict[str, str], show_test_prefix: bool = False) -> html.Div:
    """Build a single channel item with checkbox and preview info."""
    label = ch_info["label"]
    key = ch_info["key"]
    peak = ch_info.get("peak", "")
    unit = ch_info.get("unit", "")

    preview = f"  ({peak} {unit})" if peak else ""

    return html.Div(
        [
            dbc.Checkbox(
                id={"type": "channel-check", "index": key},
                label=html.Span(
                    [
                        html.Span(label, style={"fontSize": "12px"}),
                        html.Span(
                            preview,
                            style={"fontSize": "10px", "color": "#999", "marginLeft": "4px"},
                        ),
                    ]
                ),
                value=False,
                style={"marginBottom": "1px"},
            ),
        ],
        className="channel-item",
    )


def _make_group_header(
    group_label: str,
    group_id: str,
    channel_keys: list[str],
) -> html.Div:
    """Build a measurement group header with select-all button."""
    return html.Div(
        [
            html.Span(group_label, style={"fontSize": "12px", "fontWeight": "500"}),
            dbc.Button(
                "All",
                id={"type": "select-group-btn", "index": group_id},
                color="link",
                size="sm",
                style={"fontSize": "10px", "padding": "0 4px", "textDecoration": "none"},
            ),
            # Hidden store for this group's channel keys
            dcc.Store(
                id={"type": "group-channels", "index": group_id},
                data=channel_keys,
            ),
        ],
        className="d-flex align-items-center justify-content-between",
    )


def build_channel_tree(app_state: AppState) -> dbc.Card:
    """Build the full channel tree panel with search and accordion."""
    if app_state.is_empty:
        return dbc.Card(
            [
                dbc.CardHeader("Channels", className="fw-bold py-2"),
                dbc.CardBody(
                    html.Div("No test loaded", className="text-muted small text-center py-3"),
                ),
            ]
        )

    multi_test = len(app_state.tests) > 1
    full_tree = app_state.build_channel_tree()

    # Build accordion items
    accordion_items = []
    group_counter = 0

    for test_id, test_tree in full_tree.items():
        test_label = ""
        loaded = app_state.get_test(test_id)
        if loaded and multi_test:
            test_label = f"[{test_id}] "

        for obj_label, locations in test_tree.items():
            # Top-level accordion: test object (Driver, Vehicle Structure, etc.)
            obj_content = []

            for loc_label, measurements in locations.items():
                for meas_label, channels in measurements.items():
                    group_id = f"grp_{group_counter}"
                    group_counter += 1

                    channel_keys = [ch["key"] for ch in channels]
                    header_label = (
                        f"{loc_label} — {meas_label}" if loc_label != meas_label else meas_label
                    )

                    obj_content.append(_make_group_header(header_label, group_id, channel_keys))
                    for ch in channels:
                        obj_content.append(_make_channel_item(ch, show_test_prefix=multi_test))

                    obj_content.append(html.Hr(style={"margin": "4px 0"}))

            if obj_content:
                # Remove trailing hr
                if obj_content and isinstance(obj_content[-1], html.Hr):
                    obj_content = obj_content[:-1]

                display_label = f"{test_label}{obj_label}" if test_label else obj_label
                accordion_items.append(
                    dbc.AccordionItem(
                        html.Div(obj_content),
                        title=display_label,
                        style={"fontSize": "12px"},
                    )
                )

    return dbc.Card(
        [
            dbc.CardHeader(
                [
                    html.Span("Channels", className="fw-bold"),
                    html.Span(
                        f" ({app_state.total_channels})",
                        style={"fontSize": "11px", "color": "#999"},
                    ),
                ],
                className="py-2",
            ),
            dbc.CardBody(
                [
                    # Search
                    dbc.Input(
                        id="channel-search",
                        placeholder="Search channels...",
                        type="text",
                        size="sm",
                        className="mb-2",
                        debounce=True,
                    ),
                    # Selected channels display
                    html.Div(id="selected-channels-badges", className="mb-2"),
                    # Tree
                    html.Div(
                        dbc.Accordion(
                            accordion_items,
                            flush=True,
                            always_open=True,
                            start_collapsed=True,
                            id="channel-accordion",
                        ),
                        style={"maxHeight": "450px", "overflowY": "auto"},
                        id="channel-tree-container",
                    ),
                ],
                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 key in selected_keys[:20]:  # Limit display to 20
        # Extract label
        if "::" in key:
            test_id, ch_name = key.split("::", 1)
            ch = app_state.get_channel(test_id, ch_name)
            if ch and ch.code.is_valid:
                label = ch.code.short_label
            else:
                label = ch_name
            if len(app_state.tests) > 1:
                label = f"{test_id}: {label}"
        else:
            label = key

        badges.append(
            dbc.Badge(
                label,
                id={"type": "channel-badge", "index": key},
                color="primary",
                pill=True,
                className="me-1 mb-1",
                style={"fontSize": "10px", "cursor": "pointer"},
            )
        )

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

    return badges
