"""Keyboard input modes for TUI pages (navigation, slash commands, text entry)."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal, Protocol

from pygittools.tui.changes_list import ChangesList
from pygittools.tui.keys import (
	KeyLike,
	is_backspace,
	is_down,
	is_enter,
	is_esc,
	is_left,
	is_right,
	is_slash,
	is_up,
	move_hint,
	printable_char,
	set_vim_enabled,
	tab_hint,
)
from pygittools.tui.log_list import CommitLogList
from pygittools.tui.pages.actions import actions_page
from pygittools.tui.pages.branches import branch_picker_page
from pygittools.tui.pages.help import help_page
from pygittools.tui.pages.projects import projects_page
from pygittools.tui.pages.worktree_add import worktree_add_page
from pygittools.tui.types import PageAction, PageContext, PageResult

if TYPE_CHECKING:
	from pygittools.tui.tasks_list import TasksBoardList
	from pygittools.tui.worktrees_list import WorktreesList

_HeaderFocus = Literal["changes", "repo", "branch", "actions"]
_HomeTab = Literal["status", "log", "tasks", "worktrees"]
_ModeName = Literal["nav", "input", "command"]

_COMMAND_HELP = "help actions quit commit vim novim status log tasks worktrees projects branch"


@dataclass(frozen=True, slots=True)
class ContextDispatch:
	page_result: PageResult | None = None
	next_mode: _ModeName | None = None
	refresh_log: bool = False
	command_error: str | None = None


class PageInputHost(Protocol):
	@property
	def header_focus(self) -> _HeaderFocus: ...

	def set_header_focus(self, focus: _HeaderFocus) -> None: ...

	@property
	def page_context(self) -> PageContext | None: ...

	@property
	def changes(self) -> ChangesList: ...

	@property
	def log(self) -> CommitLogList: ...

	@property
	def active_tab(self) -> _HomeTab: ...

	def set_active_tab(self, tab: _HomeTab) -> None: ...

	@property
	def tasks(self) -> TasksBoardList: ...

	@property
	def worktrees(self) -> WorktreesList: ...

	@property
	def has_actions(self) -> bool: ...


class NavContext:
	def status_hint(self, host: PageInputHost) -> str:
		suffix = f"{move_hint()} — {tab_hint()} — / command"
		if host.header_focus == "repo":
			return f"Enter browse projects — {suffix}"
		if host.header_focus == "branch":
			if host.active_tab == "tasks":
				return f"Enter sync tasks — {suffix}"
			if host.active_tab == "worktrees":
				return f"Enter add worktree — {suffix}"
			return f"Enter switch branch — {suffix}"
		if host.header_focus == "actions":
			if host.has_actions:
				return f"Enter open actions — {suffix}"
			return f"No action groups — {suffix}"
		if host.active_tab == "log":
			return host.log.status_hint()
		if host.active_tab == "tasks":
			return host.tasks.status_hint()
		if host.active_tab == "worktrees":
			return host.worktrees.status_hint()
		return host.changes.status_hint()

	def dispatch_key(self, host: PageInputHost, key: KeyLike) -> ContextDispatch:
		if is_slash(key):
			return ContextDispatch(next_mode="command")

		if _try_switch_tab(host, key):
			return ContextDispatch()

		if host.header_focus == "repo":
			if is_down(key):
				host.set_header_focus("branch")
			elif is_enter(key):
				return ContextDispatch(page_result=PageResult(action=PageAction.PUSH, next_page=projects_page()))
			return ContextDispatch()

		if host.header_focus == "branch":
			if is_up(key):
				host.set_header_focus("repo")
			elif is_down(key):
				host.set_header_focus("actions" if _show_actions_row(host) else "changes")
			elif is_enter(key):
				if host.active_tab == "tasks" and host.page_context is not None:
					host.tasks.sync_with_default_remote(host.page_context.repo)
					return ContextDispatch()
				if host.active_tab == "worktrees":
					return ContextDispatch(
						page_result=PageResult(action=PageAction.PUSH, next_page=worktree_add_page()),
					)
				return ContextDispatch(
					page_result=PageResult(action=PageAction.PUSH, next_page=branch_picker_page()),
				)
			return ContextDispatch()

		if host.header_focus == "actions":
			if is_up(key):
				host.set_header_focus("branch")
			elif is_down(key):
				host.set_header_focus("changes")
			elif is_enter(key) and host.has_actions:
				return ContextDispatch(page_result=PageResult(action=PageAction.PUSH, next_page=actions_page()))
			return ContextDispatch()

		if is_up(key) and _content_list_is_at_top(host):
			host.set_header_focus("actions" if _show_actions_row(host) else "branch")
			return ContextDispatch()

		ctx = host.page_context
		if ctx is None:
			return ContextDispatch()
		if host.active_tab == "log":
			host.log.handle_nav_key(key, ctx.repo)
			return ContextDispatch()
		if host.active_tab == "tasks":
			page_result = host.tasks.handle_nav_key(key, ctx.repo)
			return ContextDispatch(page_result=page_result)
		if host.active_tab == "worktrees":
			page_result = host.worktrees.handle_nav_key(key, ctx.repo)
			return ContextDispatch(page_result=page_result)
		nav_result = host.changes.handle_nav_key(key, ctx.repo)
		if nav_result.enter_input:
			return ContextDispatch(next_mode="input")
		return ContextDispatch(refresh_log=nav_result.committed)


class CommandContext:
	def __init__(self) -> None:
		self._buffer = ""
		self._error: str | None = None

	def status_hint(self, host: PageInputHost) -> str:
		del host
		if self._error is not None:
			return self._error
		if not self._buffer:
			return f"/ — {_COMMAND_HELP} — Esc cancel"
		return f"/{self._buffer}"

	def dispatch_key(self, host: PageInputHost, key: KeyLike) -> ContextDispatch:
		if is_esc(key):
			return ContextDispatch(next_mode="nav")
		if is_backspace(key):
			self._error = None
			self._buffer = self._buffer[:-1]
			return ContextDispatch()
		if is_enter(key):
			return self._run(host)
		char = printable_char(key)
		if char is not None and not char.isspace():
			self._error = None
			self._buffer += char
			return ContextDispatch()
		return ContextDispatch()

	def _run(self, host: PageInputHost) -> ContextDispatch:
		raw = self._buffer.strip().casefold()
		name = raw.lstrip("/")
		self._buffer = ""
		if not name:
			return ContextDispatch(next_mode="nav")

		if name in {"help", "?"}:
			return ContextDispatch(
				next_mode="nav",
				page_result=PageResult(action=PageAction.PUSH, next_page=help_page()),
			)
		if name in {"actions", "!"}:
			return ContextDispatch(
				next_mode="nav",
				page_result=PageResult(action=PageAction.PUSH, next_page=actions_page()),
			)
		if name in {"quit", "q"}:
			return ContextDispatch(next_mode="nav", page_result=PageResult(action=PageAction.QUIT))
		if name == "commit":
			host.set_active_tab("status")
			host.set_header_focus("changes")
			host.changes.begin_input()
			return ContextDispatch(next_mode="input")
		if name == "vim":
			set_vim_enabled(True)
			return ContextDispatch(next_mode="nav")
		if name in {"novim", "arrows"}:
			set_vim_enabled(False)
			return ContextDispatch(next_mode="nav")
		tab = _tab_command(name)
		if tab is not None:
			host.set_active_tab(tab)
			host.set_header_focus("changes")
			return ContextDispatch(next_mode="nav")
		if name == "projects":
			return ContextDispatch(
				next_mode="nav",
				page_result=PageResult(action=PageAction.PUSH, next_page=projects_page()),
			)
		if name == "branch":
			return ContextDispatch(
				next_mode="nav",
				page_result=PageResult(action=PageAction.PUSH, next_page=branch_picker_page()),
			)

		self._error = f"Unknown command: /{name} — {_COMMAND_HELP}"
		return ContextDispatch()


_TAB_COMMANDS: dict[str, _HomeTab] = {
	"status": "status",
	"log": "log",
	"tasks": "tasks",
	"worktrees": "worktrees",
}


def _tab_command(name: str) -> _HomeTab | None:
	return _TAB_COMMANDS.get(name)


class InputContext:
	def status_hint(self, host: PageInputHost) -> str:
		return host.changes.input_status_hint()

	def dispatch_key(self, host: PageInputHost, key: KeyLike) -> ContextDispatch:
		if is_esc(key):
			return ContextDispatch(next_mode="nav")
		ctx = host.page_context
		if ctx is not None:
			result = host.changes.handle_input_key(key, ctx.repo)
			if result.relinquish:
				return ContextDispatch(next_mode="nav", refresh_log=result.committed)
		return ContextDispatch()


InputMode = NavContext | InputContext | CommandContext


def nav_context() -> NavContext:
	return NavContext()


def input_context() -> InputContext:
	return InputContext()


def command_context() -> CommandContext:
	return CommandContext()


def _show_actions_row(host: PageInputHost) -> bool:
	return host.active_tab == "status"


def _content_list_is_at_top(host: PageInputHost) -> bool:
	if host.active_tab == "log":
		return host.log.is_at_top()
	if host.active_tab == "tasks":
		return host.tasks.is_at_top()
	if host.active_tab == "worktrees":
		return host.worktrees.is_at_top()
	return host.changes.is_at_top()


def _try_switch_tab(host: PageInputHost, key: KeyLike) -> bool:
	tabs: tuple[_HomeTab, ...] = ("status", "log", "tasks", "worktrees")
	current = host.active_tab
	index = tabs.index(current)
	if is_left(key):
		if index > 0:
			host.set_active_tab(tabs[index - 1])
		return True
	if is_right(key):
		if index < len(tabs) - 1:
			host.set_active_tab(tabs[index + 1])
		return True
	return False