from __future__ import annotations

from dataclasses import dataclass
from typing import Literal

import pytest
from pygittools.tui.ucurses import KEY_DOWN, KEY_ENTER, KEY_LEFT, KEY_RIGHT, KEY_UP

from pygittools.tui.changes_list import ChangesList
from pygittools.tui.input_context import InputContext, input_context, nav_context
from pygittools.tui.log_list import CommitLogList
from pygittools.tui.pages.help import HelpPage
from pygittools.tui.types import PageAction, PageContext

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


@dataclass
class FakeHost:
	page_context: PageContext | None
	changes: ChangesList
	log: CommitLogList
	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)
	return FakeHost(page_context=page_ctx, changes=changes, log=log)


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, ord("?"))
	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._cursor = 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._cursor >= 0


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


def test_input_context_esc_returns_nav(host: FakeHost) -> None:
	dispatch = input_context().dispatch_key(host, 27)
	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()))


def test_nav_context_without_page_context() -> None:
	host = FakeHost(page_context=None, changes=ChangesList(), log=CommitLogList())
	assert nav_context().dispatch_key(host, KEY_DOWN).page_result is None