"""Filterable local branch picker."""

from __future__ import annotations

from pyratatui import (
	Constraint,
	Direction,
	Frame,
	Layout,
	List,
	ListItem,
	ListState,
	Paragraph,
	Rect,
	TextPrompt,
	TextState,
)

from pygittools.tui.branches import (
	checkout_branch,
	create_branch_from_current,
	current_branch_name,
	list_local_branches,
)
from pygittools.tui.git_colors import current_theme, focus_style
from pygittools.tui.keys import KeyLike, is_down, is_enter, is_esc, is_quit, is_up
from pygittools.tui.types import PageAction, PageContext, PageResult


class BranchPickerPage:
	title = "branches"

	def __init__(self) -> None:
		self._ctx: PageContext | None = None
		self._filter_state = TextState()
		self._filter_state.focus()
		self._branches: list[str] = []
		self._filtered: list[str] = []
		self._list_state = ListState()
		self._focus_filter = True
		self._error: str | None = None

	def on_enter(self, ctx: PageContext) -> None:
		self._ctx = ctx
		self._filter_state.set_value("")
		self._filter_state.focus()
		self._focus_filter = True
		self._error = None
		self._list_state.select(0)
		self._reload_branches()

	def status_text(self) -> str:
		if self._error:
			return self._error
		if not self._filtered:
			if self._filter_state.value().strip():
				return "Enter create branch — type name — Esc back"
			return "No matching branches — type to filter, Esc back"
		return "Enter checkout — type to filter — j/k move — Esc back"

	def draw(self, frame: Frame, area: Rect) -> None:
		if area.height <= 0:
			return
		theme = current_theme()
		chunks = (
			Layout()
			.direction(Direction.Vertical)
			.constraints([Constraint.length(1), Constraint.length(1), Constraint.fill(1)])
			.split(area)
		)
		frame.render_widget(Paragraph.from_string("Switch branch").style(theme.header), chunks[0])
		if self._focus_filter:
			self._filter_state.focus()
		else:
			self._filter_state.blur()
		frame.render_text_prompt(TextPrompt("Filter: "), chunks[1], self._filter_state)

		current = current_branch_name(self._ctx.repo) if self._ctx is not None else ""
		items: list[ListItem] = []
		selected = self._list_state.selected or 0
		for index, branch in enumerate(self._filtered):
			marker = "* " if branch == current else "  "
			focused = not self._focus_filter and index == selected
			style = focus_style(theme.normal) if focused else theme.normal
			items.append(ListItem(f"{marker}{branch}", style))
		if not items:
			items.append(ListItem("(no matches)", theme.log_date))
		frame.render_stateful_list(
			List(items).highlight_style(theme.focus).highlight_symbol(""),
			chunks[2],
			self._list_state,
		)

	def handle_key(self, key: KeyLike) -> PageResult:
		if is_esc(key) or (not self._focus_filter and is_quit(key)):
			return PageResult(action=PageAction.POP)
		if self._error is not None:
			self._error = None

		ctx = self._ctx
		if ctx is None:
			return PageResult()

		if self._focus_filter:
			if is_down(key) and self._filtered:
				self._focus_filter = False
				self._list_state.select(0)
				return PageResult()
			if is_enter(key):
				if not self._filtered:
					return self._try_create_branch(ctx)
				self._focus_filter = False
				self._list_state.select(0)
				return PageResult()
			self._filter_state.focus()
			self._filter_state.handle_key(key)
			self._apply_filter()
			return PageResult()

		if is_up(key):
			selected = self._list_state.selected or 0
			if selected <= 0:
				self._focus_filter = True
				return PageResult()
			self._list_state.select(selected - 1)
			return PageResult()
		if is_down(key):
			selected = self._list_state.selected or 0
			self._list_state.select(min(max(0, len(self._filtered) - 1), selected + 1))
			return PageResult()
		if is_enter(key) and self._filtered:
			branch = self._filtered[self._list_state.selected or 0]
			ok, error = checkout_branch(ctx.repo, branch)
			if ok:
				return PageResult(action=PageAction.POP, repo_changed=True)
			self._error = error
			return PageResult()
		# Typing jumps back to filter.
		self._focus_filter = True
		self._filter_state.focus()
		self._filter_state.handle_key(key)
		self._apply_filter()
		return PageResult()

	def _try_create_branch(self, ctx: PageContext) -> PageResult:
		ok, error = create_branch_from_current(ctx.repo, self._filter_state.value())
		if ok:
			return PageResult(action=PageAction.POP, repo_changed=True)
		self._error = error
		return PageResult()

	def _reload_branches(self) -> None:
		if self._ctx is None:
			self._branches = []
			self._filtered = []
			return
		self._branches = list_local_branches(self._ctx.repo)
		self._apply_filter()

	def _apply_filter(self) -> None:
		needle = self._filter_state.value().casefold()
		if needle:
			self._filtered = [branch for branch in self._branches if needle in branch.casefold()]
		else:
			self._filtered = list(self._branches)
		selected = self._list_state.selected or 0
		self._list_state.select(min(selected, max(0, len(self._filtered) - 1)))


def branch_picker_page() -> BranchPickerPage:
	return BranchPickerPage()