from __future__ import annotations

from dataclasses import dataclass
from typing import Literal
from unittest.mock import MagicMock

import pytest
from pyratatui import Rect

from pygittools.tui.changes_list import ChangesList
from pygittools.tui.input_context import InputContext, input_context, nav_context
from pygittools.tui.keys import key
from pygittools.tui.log_list import CommitLogList
from pygittools.tui.pages.help import HelpPage
from pygittools.tui.tasks_list import TasksBoardList
from pygittools.tui.types import PageAction, PageContext
from pygittools.tui.worktrees_list import WorktreesList

_HeaderFocus = Literal["changes", "repo", "branch"]
_HomeTab = Literal["status", "log", "tasks", "worktrees"]


@dataclass
class FakeHost:
	page_context: PageContext | None
	changes: ChangesList
	log: CommitLogList
	tasks: TasksBoardList
	worktrees: WorktreesList
	header_focus: _HeaderFocus = "changes"
	active_tab: _HomeTab = "status"

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

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


@pytest.fixture
def host(page_ctx: PageContext) -> FakeHost:
	changes = ChangesList()
	changes.refresh(page_ctx.repo)
	log = CommitLogList()
	log.refresh(page_ctx.repo)
	tasks = TasksBoardList()
	tasks.refresh(page_ctx.repo)
	worktrees = WorktreesList()
	worktrees.refresh(page_ctx.repo)
	return FakeHost(page_context=page_ctx, changes=changes, log=log, tasks=tasks, worktrees=worktrees)


def test_nav_context_status_hints(host: FakeHost) -> None:
	nav = nav_context()
	host.header_focus = "repo"
	assert "browse projects" in nav.status_hint(host)
	host.header_focus = "branch"
	assert "switch branch" in nav.status_hint(host)
	host.active_tab = "log"
	host.header_focus = "changes"
	assert "j/k move" in nav.status_hint(host)


def test_nav_context_opens_help(host: FakeHost) -> None:
	dispatch = nav_context().dispatch_key(host, key("?"))
	assert dispatch.page_result is not None
	assert dispatch.page_result.action == PageAction.PUSH
	assert isinstance(dispatch.page_result.next_page, HelpPage)


def test_nav_context_repo_header(host: FakeHost) -> None:
	host.header_focus = "repo"
	nav = nav_context()
	nav.dispatch_key(host, key("Down"))
	assert host.header_focus == "branch"
	dispatch = nav.dispatch_key(host, key("Enter"))
	assert dispatch.page_result is not None
	assert dispatch.page_result.action == PageAction.PUSH


def test_nav_context_branch_header(host: FakeHost) -> None:
	host.header_focus = "branch"
	nav = nav_context()
	nav.dispatch_key(host, key("Up"))
	assert host.header_focus == "repo"
	nav.dispatch_key(host, key("Down"))
	assert host.header_focus == "branch"
	nav.dispatch_key(host, key("Down"))
	assert host.header_focus == "changes"
	host.header_focus = "branch"
	dispatch = nav.dispatch_key(host, key("Enter"))
	assert dispatch.page_result is not None
	assert dispatch.page_result.action == PageAction.PUSH


def test_nav_context_tab_switch(host: FakeHost) -> None:
	nav = nav_context()
	host.active_tab = "log"
	nav.dispatch_key(host, key("Left"))
	assert host.active_tab == "status"
	host.active_tab = "status"
	nav.dispatch_key(host, key("Right"))
	assert host.active_tab == "log"


def test_nav_context_moves_to_branch_from_top(host: FakeHost) -> None:
	host.changes._list_state.select(0)
	nav_context().dispatch_key(host, key("Up"))
	assert host.header_focus == "branch"


def test_nav_context_log_tab(host: FakeHost) -> None:
	host.active_tab = "log"
	nav_context().dispatch_key(host, key("Down"))
	assert (host.log._list_state.selected or 0) >= 0


def test_nav_context_enters_input(host: FakeHost) -> None:
	host.changes._list_state.select(0)
	dispatch = nav_context().dispatch_key(host, key("a"))
	assert dispatch.next_mode == "input"


def test_input_context_esc_returns_nav(host: FakeHost) -> None:
	dispatch = input_context().dispatch_key(host, key("Esc"))
	assert dispatch.next_mode == "nav"


def test_input_context_commit_relinquishes(host: FakeHost, monkeypatch: pytest.MonkeyPatch) -> None:
	host.changes.set_commit_message("msg")
	monkeypatch.setattr(
		"pygittools.tui.changes_list.commit_staged",
		lambda _repo, _msg: (True, ""),
	)
	dispatch = input_context().dispatch_key(host, key("Enter"))
	assert dispatch.next_mode == "nav"


def test_input_context_status_hint() -> None:
	assert "Esc" in InputContext().status_hint(
		FakeHost(None, ChangesList(), CommitLogList(), TasksBoardList(), WorktreesList()),
	)


def test_nav_context_without_page_context() -> None:
	host = FakeHost(
		page_context=None,
		changes=ChangesList(),
		log=CommitLogList(),
		tasks=TasksBoardList(),
		worktrees=WorktreesList(),
	)
	assert nav_context().dispatch_key(host, key("Down")).page_result is None


def test_draw_helpers_unused() -> None:
	# keep import of Rect available for call sites in other modules
	assert Rect(0, 0, 1, 1).width == 1
	assert MagicMock() is not None