"""Collapsible task board view for the pgt TUI."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal

from pygit2 import Repository
from pyratatui import Frame, List, ListItem, ListState, Rect, Style

from pygittools.tags_sync import format_sync_result, sync_board_tags
from pygittools.tasks_query import (
    BOARD_STATUS_ORDER,
    BoardColumnView,
    BoardView,
    get_board_tasks_grouped,
    list_boards,
)
from pygittools.tui.git_colors import current_theme, focus_style
from pygittools.tui.keys import KeyLike, board_hint, is_down, is_enter, is_left, is_right, is_space, is_up, move_hint
from pygittools.tui.types import PageAction, PageResult

if TYPE_CHECKING:
    from pygittools.tui.pages.task_editor import TaskEditorPage


@dataclass(frozen=True, slots=True)
class _Row:
    kind: Literal["board", "status_header", "new_task", "task"]
    status: str | None
    task_ref: str | None
    label: str


class TasksBoardList:
    def __init__(self) -> None:
        self._boards: list[BoardView] = []
        self._board_index = 0
        self._board_name: str | None = None
        self._columns: list[BoardColumnView] = []
        self._collapsed: dict[str, bool] = {status: False for status in BOARD_STATUS_ORDER}
        self._rows: list[_Row] = []
        self._list_state = ListState()
        self._error: str | None = None

    @property
    def board_name(self) -> str | None:
        return self._board_name

    def is_at_top(self) -> bool:
        return (self._list_state.selected or 0) == 0

    def refresh(self, repo: Repository) -> None:
        self._boards = list_boards(repo)
        if not self._boards:
            self._columns = []
            self._rows = []
            self._board_name = None
            self._list_state.select(0)
            return

        if self._board_name is None:
            self._board_index = _default_board_index(self._boards)
        else:
            self._board_index = _board_index_for_name(self._boards, self._board_name)
        self._board_index = max(0, min(self._board_index, len(self._boards) - 1))
        self._board_name = self._boards[self._board_index]["name"]

        try:
            self._columns = get_board_tasks_grouped(repo, self._board_name or "")
        except KeyError as exc:
            self._error = str(exc)
            self._columns = []
        self._rebuild_rows()

    def _rebuild_rows(self) -> None:
        if not self._boards:
            self._rows = []
            self._list_state.select(0)
            return

        board = self._boards[self._board_index]
        rows: list[_Row] = [
            _Row(
                kind="board",
                status=None,
                task_ref=None,
                label=f"Board: {board['name']} ({board['task_count']} tasks)",
            ),
        ]
        for column in self._columns:
            status = column["status"]
            label = column["label"]
            tasks = column["tasks"]
            if status not in self._collapsed:
                self._collapsed[status] = False
            marker = ">" if self._collapsed[status] else "v"
            header = f"[{marker}] {label} ({len(tasks)})"
            rows.append(_Row(kind="status_header", status=status, task_ref=None, label=header))
            if self._collapsed[status]:
                continue
            rows.append(
                _Row(
                    kind="new_task",
                    status=status,
                    task_ref=None,
                    label="(+ New Task)",
                ),
            )
            for task in tasks:
                title = task.get("title") or "(untitled)"
                rows.append(
                    _Row(
                        kind="task",
                        status=status,
                        task_ref=task.get("ref"),
                        label=f"  {title}",
                    ),
                )

        self._rows = rows
        selected = self._list_state.selected or 0
        self._list_state.select(min(selected, max(0, len(self._rows) - 1)))

    def draw(self, frame: Frame, area: Rect, *, highlight: bool) -> None:
        if area.height <= 0 or area.width <= 0:
            return
        theme = current_theme()
        if not self._rows and not self._error:
            frame.render_stateful_list(
                List([ListItem("No task boards — use pygittools tasks to create one", theme.log_date)]),
                area,
                self._list_state,
            )
            return

        items: list[ListItem] = []
        error_offset = 0
        if self._error:
            items.append(ListItem(self._error, theme.log_date))
            error_offset = 1
        selected = self._list_state.selected or 0
        for index, row in enumerate(self._rows):
            focused = highlight and index == selected
            style = _row_style(row, focused=focused, highlight=highlight)
            items.append(ListItem(row.label, style))
        list_state = self._list_state
        if error_offset:
            list_state = ListState()
            row_selected = self._list_state.selected
            if row_selected is not None:
                list_state.select(row_selected + error_offset)
        frame.render_stateful_list(
            List(items).highlight_style(theme.focus).highlight_symbol(""),
            area,
            list_state,
        )

    def sync_with_default_remote(self, repo: Repository) -> None:
        remote_names = [name for name in repo.remotes.names() if name]
        if not remote_names:
            self._error = "No remotes configured for task sync"
            return
        remote_name = "origin" if "origin" in remote_names else remote_names[0]
        board_name = self._board_name or "Tasks"
        try:
            result = sync_board_tags(repo, remote_name, board_name)
        except (KeyError, RuntimeError, ValueError) as exc:
            self._error = str(exc)
            return
        self._error = format_sync_result(result)
        self.refresh(repo)

    def handle_nav_key(self, key: KeyLike, repo: Repository) -> PageResult | None:
        if self._error is not None:
            self._error = None
        if not self._rows and not self._boards:
            self.refresh(repo)
            return None

        if is_up(key):
            selected = self._list_state.selected or 0
            self._list_state.select(max(0, selected - 1))
            return None
        if is_down(key):
            selected = self._list_state.selected or 0
            self._list_state.select(min(max(0, len(self._rows) - 1), selected + 1))
            return None
        if not self._rows:
            return None

        row = self._rows[self._list_state.selected or 0]

        if row.kind == "board" and self._boards:
            if is_left(key) and self._board_index > 0:
                self._board_index -= 1
                self._board_name = self._boards[self._board_index]["name"]
                self._list_state.select(0)
                self.refresh(repo)
                return None
            if is_right(key) and self._board_index < len(self._boards) - 1:
                self._board_index += 1
                self._board_name = self._boards[self._board_index]["name"]
                self._list_state.select(0)
                self.refresh(repo)
                return None
            return None

        if row.kind == "status_header" and row.status is not None:
            if is_left(key) or is_right(key) or is_space(key) or is_enter(key):
                self._collapsed[row.status] = not self._collapsed.get(row.status, False)
                self._rebuild_rows()
            return None

        if is_enter(key):
            if row.kind == "new_task" and row.status is not None and self._board_name is not None:
                return PageResult(
                    action=PageAction.PUSH,
                    next_page=_task_editor_page(self._board_name, None, default_status=row.status),
                )
            if row.kind == "task" and row.task_ref is not None and self._board_name is not None:
                return PageResult(
                    action=PageAction.PUSH,
                    next_page=_task_editor_page(self._board_name, row.task_ref, default_status=row.status),
                )
        return None

    def status_hint(self) -> str:
        if self._error:
            return self._error
        if not self._boards:
            return "No task boards — use pygittools tasks to create one"
        if not self._rows:
            return "No tasks — use (+ New Task) to create one"
        move = move_hint()
        row = self._rows[self._list_state.selected or 0]
        if row.kind == "board":
            return f"{board_hint()} — {move}"
        if row.kind == "status_header":
            return f"Enter collapse — Space collapse — {move}"
        if row.kind == "new_task":
            return f"Enter create task — {move}"
        return f"Enter edit task — {move}"


def _default_board_index(boards: list[BoardView]) -> int:
    for index, board in enumerate(boards):
        if board["name"].casefold() == "tasks":
            return index
    return 0


def _board_index_for_name(boards: list[BoardView], name: str | None) -> int:
    if name is None:
        return _default_board_index(boards)
    for index, board in enumerate(boards):
        if board["name"] == name:
            return index
    return _default_board_index(boards)


def _row_style(row: _Row, *, focused: bool, highlight: bool) -> Style:
    theme = current_theme()
    if row.kind == "board":
        base = theme.header
    elif row.kind == "status_header":
        base = theme.section_header
    else:
        base = theme.normal
    if focused and highlight:
        return focus_style(base)
    return base


def _task_editor_page(board_name: str, task_ref: str | None, *, default_status: str | None) -> TaskEditorPage:
    from pygittools.tui.pages.task_editor import TaskEditorPage

    return TaskEditorPage(board_name=board_name, task_ref=task_ref, default_status=default_status)