from __future__ import annotations

from pathlib import Path

import pytest
from pygit2 import Repository

from pygittools.tui.changes_list import ChangesList, SectionId
from pygittools.tui.types import PageContext
from pygittools.tui.ucurses import KEY_BACKSPACE, KEY_DOWN, KEY_ENTER, KEY_RESIZE, KEY_UP
from pygittools.tui.worktree import categorize_status


def test_refresh_builds_sections(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.refresh(committed_repo)
	assert changes.status_hint() != ""


def test_refresh_clean_repo(page_ctx: PageContext) -> None:
	changes = ChangesList()
	changes.refresh(page_ctx.repo)
	assert changes.status_hint() == "Enter type message — j/k move"


def test_nav_up_down(page_ctx: PageContext) -> None:
	changes = ChangesList()
	changes.refresh(page_ctx.repo)
	changes.handle_nav_key(KEY_DOWN, page_ctx.repo)
	changes.handle_nav_key(KEY_UP, page_ctx.repo)
	assert changes.is_at_top()


def test_nav_resize_is_noop(page_ctx: PageContext) -> None:
	changes = ChangesList()
	assert changes.handle_nav_key(KEY_RESIZE, page_ctx.repo).layout_changed is False


def test_stage_and_unstage_file(committed_repo: Repository) -> None:
	path = Path(committed_repo.workdir) / "tracked.txt"
	path.write_text("v2\n", encoding="utf-8")
	changes = ChangesList()
	changes.refresh(committed_repo)
	changes._cursor = 3
	changes.handle_nav_key(KEY_ENTER, committed_repo)
	changes.refresh(committed_repo)
	staged, unstaged, _ = categorize_status(committed_repo)
	assert "tracked.txt" in staged or "tracked.txt" in unstaged


def test_toggle_section_stages_untracked(committed_repo: Repository) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	changes = ChangesList()
	changes.refresh(committed_repo)
	for index, row in enumerate(changes._rows):
		if row.kind == "header" and row.section == SectionId.UNTRACKED:
			changes._cursor = index
			break
	result = changes.handle_nav_key(KEY_ENTER, committed_repo)
	assert result.layout_changed is True


def test_collapse_section_with_space(committed_repo: Repository) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	changes = ChangesList()
	changes.refresh(committed_repo)
	for index, row in enumerate(changes._rows):
		if row.kind == "header" and row.section == SectionId.UNSTAGED:
			changes._cursor = index
			break
	result = changes.handle_nav_key(ord(" "), committed_repo)
	assert result.layout_changed is True


def test_commit_input_flow(committed_repo: Repository, monkeypatch: pytest.MonkeyPatch) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	committed_repo.index.add("new.txt")
	committed_repo.index.write()
	changes = ChangesList()
	changes.refresh(committed_repo)
	changes._cursor = 0
	changes.handle_nav_key(ord("f"), committed_repo)
	assert changes.commit_message == "f"
	changes.handle_input_key(KEY_BACKSPACE, committed_repo)
	assert changes.commit_message == ""
	monkeypatch.setattr(
		"pygittools.tui.changes_list.commit_staged",
		lambda _repo, _msg: (True, ""),
	)
	result = changes.handle_input_key(KEY_ENTER, committed_repo)
	assert result.relinquish is True


def test_commit_empty_shows_error(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.refresh(committed_repo)
	changes._cursor = 0
	changes.handle_input_key(KEY_ENTER, committed_repo)
	assert changes.status_hint() == "Commit message is empty"


def test_commit_from_nav_row(committed_repo: Repository, monkeypatch: pytest.MonkeyPatch) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	committed_repo.index.add("new.txt")
	committed_repo.index.write()
	changes = ChangesList()
	changes.set_commit_message("ship it")
	changes.refresh(committed_repo)
	changes._cursor = 0
	monkeypatch.setattr(
		"pygittools.tui.changes_list.commit_staged",
		lambda _repo, _msg: (True, ""),
	)
	result = changes.handle_nav_key(KEY_ENTER, committed_repo)
	assert result.layout_changed is True


def test_enter_on_empty_commit_enters_input(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.refresh(committed_repo)
	changes._cursor = 0
	result = changes.handle_nav_key(KEY_ENTER, committed_repo)
	assert result.enter_input is True


def test_status_hints_for_sections(committed_repo: Repository) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	changes = ChangesList()
	changes.refresh(committed_repo)
	for index, row in enumerate(changes._rows):
		if row.kind == "header" and row.section == SectionId.STAGED:
			changes._cursor = index
			assert "stage all" in changes.status_hint()
		if row.kind == "file" and row.section == SectionId.UNTRACKED:
			changes._cursor = index
			assert "Enter stage" in changes.status_hint()


def test_draw_and_scroll(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.refresh(committed_repo)
	changes.draw(0, 0, 80)
	changes._cursor = 50
	changes.draw(0, 5, 80, highlight=True, commit_input=True)


def test_persist_commit_draft(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.set_commit_message("draft")
	changes.persist_commit_draft(committed_repo)
	from pygittools.tui.commit_draft import load_commit_draft

	assert load_commit_draft(Path(committed_repo.path), committed_repo) == "draft"


def test_unstage_all_from_empty_staged_header(committed_repo: Repository) -> None:
	(Path(committed_repo.workdir) / "new.txt").write_text("x\n", encoding="utf-8")
	changes = ChangesList()
	changes.refresh(committed_repo)
	for index, row in enumerate(changes._rows):
		if row.kind == "header" and row.section == SectionId.STAGED:
			changes._cursor = index
			break
	result = changes.handle_nav_key(KEY_ENTER, committed_repo)
	assert result.layout_changed is True


def test_long_commit_message_truncates_in_label(committed_repo: Repository) -> None:
	changes = ChangesList()
	changes.set_commit_message("x" * 200)
	label = changes._commit_label(40, focused=True)
	assert len(label) <= 40