"""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 pyratatui import Frame, List, ListItem, ListState, Rect

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


@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._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._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"]

		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
			selected = self._list_state.selected or 0
			self._list_state.select(min(selected, max(0, len(self._rows) - 1)))
			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
		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.refresh(repo)
			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"
		row = self._rows[self._list_state.selected or 0]
		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 _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):
	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):
	from pygittools.tui.pages.task_editor import TaskEditorPage

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