"""Linked worktree list for the pgt TUI."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Literal

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

from pygittools.tui.git_colors import current_theme, focus_style
from pygittools.tui.git_worktrees import WorktreeInfo, list_worktrees, remove_worktree
from pygittools.tui.keys import KeyLike, is_down, is_enter, is_up
from pygittools.tui.pages.worktree_add import worktree_add_page
from pygittools.tui.types import PageAction, PageResult


@dataclass(frozen=True, slots=True)
class _Row:
	kind: Literal["add", "worktree"]
	info: WorktreeInfo | None


class WorktreesList:
	def __init__(self) -> None:
		self._rows: list[_Row] = []
		self._list_state = ListState()
		self._error: str | None = None
		self._current_path: Path | None = None

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

	def refresh(self, repo: Repository) -> None:
		workdir = repo.workdir
		self._current_path = Path(workdir).resolve() if workdir is not None else None
		entries, error = list_worktrees(repo)
		self._error = error
		rows: list[_Row] = [_Row(kind="add", info=None)]
		for info in entries:
			rows.append(_Row(kind="worktree", info=info))
		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()
		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):
			label = _row_label(row, current=self._current_path, width=max(10, area.width - 4))
			focused = highlight and index == selected
			style = focus_style(theme.normal) if focused and highlight else theme.normal
			if row.kind == "add":
				style = focus_style(theme.header) if focused and highlight else theme.header
			items.append(ListItem(label, style))
		if not self._rows and not self._error:
			items.append(ListItem("No worktrees", theme.log_date))
		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 handle_nav_key(self, key: KeyLike, repo: Repository) -> PageResult | None:
		if self._error is not None and key.code not in {"Up", "Down", "k", "K", "j", "J"}:
			self._error = 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 is_enter(key):
			if row.kind == "add":
				return PageResult(action=PageAction.PUSH, next_page=worktree_add_page())
			if row.info is not None and self._current_path is not None:
				target = row.info.path.resolve()
				if target != self._current_path.resolve():
					return PageResult(action=PageAction.PUSH, switch_repo=target)
			return None
		if key.code in {"d", "D"} and row.kind == "worktree" and row.info is not None:
			if self._current_path is not None and row.info.path.resolve() == self._current_path.resolve():
				self._error = "Cannot remove the active worktree"
				return None
			ok, error = remove_worktree(repo, row.info.path, force=True)
			if ok:
				self.refresh(repo)
				return None
			self._error = error
		return None

	def status_hint(self) -> str:
		if self._error:
			return self._error
		if not self._rows:
			return "No worktrees"
		row = self._rows[self._list_state.selected or 0]
		if row.kind == "add":
			return "Enter add worktree — j/k move"
		if row.info is not None and self._current_path is not None:
			if row.info.path.resolve() == self._current_path.resolve():
				return "Active worktree — d remove other worktrees — j/k move"
			return "Enter open worktree — d remove — j/k move"
		return "j/k move"


def _row_label(row: _Row, *, current: Path | None, width: int) -> str:
	if row.kind == "add":
		return "(+ Add worktree)"
	if row.info is None:
		return ""
	info = row.info
	marker = "* " if current is not None and info.path.resolve() == current.resolve() else "  "
	if info.bare:
		branch = "(bare)"
	elif info.branch:
		branch = info.branch
	elif info.detached:
		branch = "(detached)"
	else:
		branch = "?"
	extras: list[str] = []
	if info.locked is not None:
		extras.append("locked")
	if info.prunable is not None:
		extras.append("prunable")
	suffix = f" [{', '.join(extras)}]" if extras else ""
	path_str = _display_path(info.path, anchor=current)
	label = f"{marker}{path_str}  {branch}{suffix}"
	if len(label) > width:
		return label[: max(0, width - 1)] + "…"
	return label


def _display_path(path: Path, *, anchor: Path | None) -> str:
	if anchor is not None:
		try:
			return str(path.resolve().relative_to(anchor.parent.resolve()))
		except ValueError:
			pass
	return str(path)