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

from __future__ import annotations

from dataclasses import dataclass
from typing import Literal

from pygit2 import Repository

from pygittools.tasks_query import BOARD_STATUS_ORDER, BoardView, get_board_tasks_grouped, list_boards
from pygittools.tui.draw import draw_line
from pygittools.tui.git_colors import current_theme, focus_attr
from pygittools.tui.types import PageAction, PageResult
from pygittools.tui.ucurses import (  # type: ignore[import-untyped]
	KEY_DOWN,
	KEY_ENTER,
	KEY_LEFT,
	KEY_RESIZE,
	KEY_RIGHT,
	KEY_UP,
)


@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._collapsed: dict[str, bool] = {status: False for status in BOARD_STATUS_ORDER}
		self._rows: list[_Row] = []
		self._cursor = 0
		self._scroll_offset = 0
		self._error: str | None = None

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

	def is_at_top(self) -> bool:
		return self._cursor == 0

	def refresh(self, repo: Repository) -> None:
		self._boards = list_boards(repo)
		if not self._boards:
			self._rows = []
			self._board_name = None
			self._cursor = 0
			self._scroll_offset = 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"]

		rows: list[_Row] = []
		board = self._boards[self._board_index]
		board_label = f"Board: {board['name']} ({board['task_count']} tasks)"
		rows.append(_Row(kind="board", status=None, task_ref=None, label=board_label))

		try:
			columns = get_board_tasks_grouped(repo, self._board_name or "")
		except KeyError as exc:
			self._error = str(exc)
			self._rows = rows
			self._cursor = min(self._cursor, max(0, len(self._rows) - 1))
			self._scroll_offset = 0
			return

		for column in 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
		self._cursor = min(self._cursor, max(0, len(self._rows) - 1))
		self._ensure_cursor_visible(10)

	def draw(self, top_row: int, height: int, width: int, *, highlight: bool) -> None:
		if height <= 0:
			return
		self._ensure_cursor_visible(height)

		if not self._rows and not self._error:
			draw_line(
				top_row, 0, "No task boards — use pygittools tasks to create one", width, current_theme().log_date
			)
			return

		if self._error:
			draw_line(top_row, 0, self._error[:width], width, current_theme().log_date)

		for view_row in range(height):
			row_index = self._scroll_offset + view_row
			if row_index >= len(self._rows):
				break
			row = self._rows[row_index]
			focused = highlight and row_index == self._cursor
			screen_row = top_row + view_row
			attr = _row_attr(row, focused=focused, highlight=highlight)
			draw_line(screen_row, 0, row.label, width, attr)

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

		if key in (KEY_UP, ord("k"), ord("K")):
			self._cursor = max(0, self._cursor - 1)
			self._ensure_cursor_visible(10)
			return None
		if key in (KEY_DOWN, ord("j"), ord("J")):
			self._cursor = min(max(0, len(self._rows) - 1), self._cursor + 1)
			self._ensure_cursor_visible(10)
			return None
		if not self._rows:
			return None

		row = self._rows[self._cursor]

		if row.kind == "board" and self._boards:
			if key in (KEY_LEFT, ord("h"), ord("H")) and self._board_index > 0:
				self._board_index -= 1
				self._board_name = self._boards[self._board_index]["name"]
				self._cursor = 0
				self._scroll_offset = 0
				self.refresh(repo)
				return None
			if key in (KEY_RIGHT, ord("l"), ord("L")) and self._board_index < len(self._boards) - 1:
				self._board_index += 1
				self._board_name = self._boards[self._board_index]["name"]
				self._cursor = 0
				self._scroll_offset = 0
				self.refresh(repo)
				return None
			return None

		if row.kind == "status_header" and row.status is not None:
			if key in (KEY_LEFT, KEY_RIGHT, ord(" ")):
				self._collapsed[row.status] = not self._collapsed.get(row.status, False)
				self.refresh(repo)
				return None
			if key in (KEY_ENTER, 10, 13):
				self._collapsed[row.status] = not self._collapsed.get(row.status, False)
				self.refresh(repo)
				return None
			return None

		if key in (KEY_ENTER, 10, 13):
			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"
		row = self._rows[self._cursor]
		if row.kind == "board":
			return "H/L switch board — j/k move"
		if row.kind == "status_header":
			return "Enter collapse — Space collapse — j/k move"
		if row.kind == "new_task":
			return "Enter create task — j/k move"
		return "Enter edit task — j/k move"

	def _ensure_cursor_visible(self, height: int) -> None:
		if height <= 0:
			return
		if self._cursor < self._scroll_offset:
			self._scroll_offset = self._cursor
		elif self._cursor >= self._scroll_offset + height:
			self._scroll_offset = self._cursor - height + 1


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_attr(row: _Row, *, focused: bool, highlight: bool) -> int:
	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_attr(base)
	return base


def _task_editor_page(board_name: str, task_ref: str | None, *, default_status: str | None):
	# Lazy import to avoid circular dependency.
	from pygittools.tui.pages.task_editor import TaskEditorPage

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