"""Tests for the actions TUI page."""

from __future__ import annotations

import sys
import time
from pathlib import Path
from unittest.mock import MagicMock

import pytest
from pygit2 import Repository, init_repository
from pyratatui import Rect

from pygittools.tui.git_colors import init_tui_color_theme
from pygittools.tui.keys import key
from pygittools.tui.pages.actions import ActionsPage, _RunState, actions_page
from pygittools.tui.types import PageAction, PageContext


def _repo_with_actions(tmp_path: Path, actions_body: str) -> PageContext:
    repo_dir = tmp_path / "repo"
    repo_dir.mkdir()
    repo = init_repository(str(repo_dir), bare=False)
    (repo_dir / "pyproject.toml").write_text(
        f'[project]\nname = "demo"\nversion = "0"\n\n[tool.pgt]\nactions = {actions_body}\n',
        encoding="utf-8",
    )
    venv_bin = repo_dir / ".venv" / "bin"
    venv_bin.mkdir(parents=True)
    (venv_bin / "python").symlink_to(sys.executable)
    return PageContext(repo=repo, repo_path=Path(repo.path))


def test_actions_page_empty(tmp_path: Path) -> None:
    repo_dir = tmp_path / "repo"
    repo_dir.mkdir()
    repo = init_repository(str(repo_dir), bare=False)
    ctx = PageContext(repo=repo, repo_path=Path(repo.path))
    page = actions_page()
    page.on_enter(ctx)
    assert "No pyproject" in page.status_text() or "No [tool.pgt]" in page.status_text()
    page.draw(MagicMock(), Rect(0, 0, 80, 20))
    assert page.handle_key(key("Esc")).action == PageAction.POP


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_actions_page_lists_and_run(tmp_path: Path) -> None:
    ctx = _repo_with_actions(tmp_path, '[{ name = "ok", script = "ok.py" }]')
    init_tui_color_theme(ctx.repo)
    workdir = Path(ctx.repo.workdir)
    (workdir / "ok.py").write_text("print('done')\n", encoding="utf-8")
    page = ActionsPage()
    page.on_enter(ctx)
    assert "Enter run" in page.status_text()
    page.draw(MagicMock(), Rect(0, 0, 80, 20))
    # select the action row (index 1; 0 is Run all)
    page.handle_key(key("Down"))
    page.handle_key(key("Enter"))
    deadline = time.monotonic() + 5.0
    while time.monotonic() < deadline:
        page.on_idle()
        with page._lock:
            if page._active is None and page._states.get(("default", "ok")) is not _RunState.IDLE:
                break
        time.sleep(0.05)
    assert page._states[("default", "ok")].name == "SUCCESS"
    assert page._messages[("default", "ok")] == "done"
    page.draw(MagicMock(), Rect(0, 0, 80, 20))


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_actions_page_run_all_stops_on_failure(tmp_path: Path) -> None:
    ctx = _repo_with_actions(
        tmp_path,
        """[
  { name = "bad", script = "bad.py" },
  { name = "good", script = "good.py" },
]""",
    )
    init_tui_color_theme(ctx.repo)
    workdir = Path(ctx.repo.workdir)
    (workdir / "bad.py").write_text("import sys\nsys.exit(1)\n", encoding="utf-8")
    (workdir / "good.py").write_text("print('should-not-run')\n", encoding="utf-8")
    page = ActionsPage()
    page.on_enter(ctx)
    page.handle_key(key("a"))
    deadline = time.monotonic() + 5.0
    while time.monotonic() < deadline:
        page.on_idle()
        with page._lock:
            if page._active is None:
                break
        time.sleep(0.05)
    assert page._states[("default", "bad")] is _RunState.FAILURE
    assert page._states[("default", "good")] is _RunState.IDLE
    assert ("default", "good") not in page._messages


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_actions_page_opens_from_factory(tmp_path: Path) -> None:
    ctx = _repo_with_actions(tmp_path, "[]")
    page = actions_page()
    page.on_enter(ctx)
    assert page.title == "actions"
    assert isinstance(ctx.repo, Repository)
    assert "No actions configured" in page.status_text()


def _repo_dir(tmp_path: Path) -> tuple[PageContext, Path]:
    repo_dir = tmp_path / "repo"
    repo_dir.mkdir()
    repo = init_repository(str(repo_dir), bare=False)
    return PageContext(repo=repo, repo_path=Path(repo.path)), repo_dir


def test_actions_page_cycles_sets(tmp_path: Path) -> None:
    ctx, repo_dir = _repo_dir(tmp_path)
    (repo_dir / "pyproject.toml").write_text(
        '[project]\nname = "demo"\n\n[tool.pgt]\nactions = [{ name = "lint", script = "lint.py" }]\n',
        encoding="utf-8",
    )
    (repo_dir / "actions.toml").write_text(
        '[release]\nactions = [{ name = "build", script = "build.py" }]\n',
        encoding="utf-8",
    )
    page = ActionsPage()
    page.on_enter(ctx)
    assert "Tab/Shift-Tab" in page.status_text()
    assert [label for _, label in page._rows()] == ["Run all", "lint"]
    page.handle_key(key("Down"))
    page.handle_key(key("Tab"))
    assert [label for _, label in page._rows()] == ["Run all", "build"]
    assert page._list_state.selected == 0
    page.handle_key(key("Tab", shift=True))
    assert [label for _, label in page._rows()] == ["Run all", "lint"]
    page.handle_key(key("BackTab"))
    assert [label for _, label in page._rows()] == ["Run all", "build"]
    page.handle_key(key("Left"))
    assert [label for _, label in page._rows()] == ["Run all", "lint"]
    page.handle_key(key("Right"))
    assert [label for _, label in page._rows()] == ["Run all", "build"]
    page.draw(MagicMock(), Rect(0, 0, 80, 20))


def test_actions_page_run_all_uses_current_set(tmp_path: Path) -> None:
    ctx, repo_dir = _repo_dir(tmp_path)
    (repo_dir / "actions.toml").write_text(
        """
[first]
actions = [{ name = "one", script = "one.py" }]

[second]
actions = [{ name = "two", script = "two.py" }]
""",
        encoding="utf-8",
    )
    page = ActionsPage()
    page.on_enter(ctx)
    page.handle_key(key("Tab"))
    page.handle_key(key("a"))
    deadline = time.monotonic() + 5.0
    while time.monotonic() < deadline:
        page.on_idle()
        with page._lock:
            if page._active is None:
                break
        time.sleep(0.05)
    assert page._states[("first", "one")] is _RunState.IDLE
    assert page._states[("second", "two")] is _RunState.FAILURE
    assert "Script not found" in page._messages[("second", "two")]


def test_actions_page_bad_actions_toml(tmp_path: Path) -> None:
    ctx, repo_dir = _repo_dir(tmp_path)
    (repo_dir / "actions.toml").write_text("quality = 1\n", encoding="utf-8")
    page = ActionsPage()
    page.on_enter(ctx)
    assert "unexpected key" in page.status_text()
    assert page.handle_key(key("Esc")).action == PageAction.POP