"""Tests for project actions API."""

from __future__ import annotations

import stat
import sys
from pathlib import Path

import pytest

from pygittools.actions import (
	Action,
	ActionSpec,
	ProjectActions,
	find_project_root,
	load_action_specs,
	project_actions,
	project_venv_python,
	run_action_script,
)


def _write_pyproject(root: Path, actions_toml: str) -> None:
	(root / "pyproject.toml").write_text(
		f'[project]\nname = "demo"\nversion = "0"\n\n[tool.pgt]\nactions = {actions_toml}\n',
		encoding="utf-8",
	)


def _make_venv(root: Path) -> Path:
	venv = root / ".venv"
	bin_dir = venv / "bin"
	bin_dir.mkdir(parents=True)
	python = bin_dir / "python"
	python.symlink_to(sys.executable)
	return python


def test_find_project_root(tmp_path: Path) -> None:
	nested = tmp_path / "a" / "b"
	nested.mkdir(parents=True)
	(tmp_path / "pyproject.toml").write_text("[project]\nname='x'\n", encoding="utf-8")
	assert find_project_root(nested) == tmp_path.resolve()
	assert find_project_root(tmp_path) == tmp_path.resolve()


def test_load_action_specs_inline_tables(tmp_path: Path) -> None:
	_write_pyproject(
		tmp_path,
		"""[
  { name = "lint", script = "scripts/lint.py" },
  { name = "test", script = "scripts/test.py" },
]""",
	)
	specs = load_action_specs(tmp_path)
	assert specs == [
		ActionSpec(name="lint", script="scripts/lint.py"),
		ActionSpec(name="test", script="scripts/test.py"),
	]


def test_load_action_specs_pair_arrays(tmp_path: Path) -> None:
	_write_pyproject(tmp_path, '[["lint", "scripts/lint.py"], ["test", "scripts/test.sh"]]')
	specs = load_action_specs(tmp_path)
	assert [s.name for s in specs] == ["lint", "test"]


def test_load_action_specs_invalid(tmp_path: Path) -> None:
	_write_pyproject(tmp_path, '{ name = "lint" }')
	with pytest.raises(ValueError, match="must be an array"):
		load_action_specs(tmp_path)


def test_project_actions_is_action(tmp_path: Path) -> None:
	_write_pyproject(tmp_path, '[{ name = "ok", script = "ok.py" }]')
	runner = project_actions(tmp_path)
	assert isinstance(runner, Action)
	assert issubclass(ProjectActions, Action)


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_python_success(tmp_path: Path) -> None:
	_make_venv(tmp_path)
	script = tmp_path / "ok.py"
	script.write_text("print('hello-ok')\n", encoding="utf-8")
	ok, message = run_action_script(tmp_path, "ok.py")
	assert ok is True
	assert message == "hello-ok"


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_python_failure(tmp_path: Path) -> None:
	_make_venv(tmp_path)
	script = tmp_path / "fail.py"
	script.write_text("import sys\nprint('boom', file=sys.stderr)\nsys.exit(2)\n", encoding="utf-8")
	ok, message = run_action_script(tmp_path, "fail.py")
	assert ok is False
	assert "boom" in message


def test_run_action_script_missing_venv(tmp_path: Path) -> None:
	script = tmp_path / "ok.py"
	script.write_text("print('x')\n", encoding="utf-8")
	ok, message = run_action_script(tmp_path, "ok.py")
	assert ok is False
	assert "venv not found" in message


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_shell(tmp_path: Path) -> None:
	_make_venv(tmp_path)
	script = tmp_path / "hi.sh"
	script.write_text("#!/bin/bash\necho shell-ok\n", encoding="utf-8")
	script.chmod(script.stat().st_mode | stat.S_IXUSR)
	ok, message = run_action_script(tmp_path, "hi.sh")
	assert ok is True
	assert message == "shell-ok"
	assert project_venv_python(tmp_path) is not None


def test_project_actions_unknown(tmp_path: Path) -> None:
	_write_pyproject(tmp_path, "[]")
	ok, message = ProjectActions(tmp_path).action("missing")
	assert ok is False
	assert "Unknown action" in message


@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_project_actions_runs_named(tmp_path: Path) -> None:
	_make_venv(tmp_path)
	_write_pyproject(tmp_path, '[{ name = "greet", script = "greet.py" }]')
	(tmp_path / "greet.py").write_text("print('hi')\n", encoding="utf-8")
	ok, message = ProjectActions(tmp_path).action("greet")
	assert ok is True
	assert message == "hi"