from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pygit2
import pytest

from pygitweb_pytesthtml import worktree as wt


def _commit(repo: pygit2.Repository, repo_dir: Path, branch: str = "main") -> None:
	content = b"hello\n"
	sig = pygit2.Signature("tester", "tester@example.com")
	if repo.is_bare:
		blob_oid = repo.create_blob(content)
		tree_builder = repo.TreeBuilder()
		tree_builder.insert("README.md", blob_oid, pygit2.GIT_FILEMODE_BLOB)
		tree_oid = tree_builder.write()
		repo.create_commit(f"refs/heads/{branch}", sig, sig, "init", tree_oid, [])
		return
	(repo_dir / "README.md").write_bytes(content)
	index = repo.index
	index.add("README.md")
	index.write()
	tree = index.write_tree()
	repo.create_commit(f"refs/heads/{branch}", sig, sig, "init", tree, [])


@pytest.fixture
def projectroot(tmp_path: Path) -> Path:
	return tmp_path


def test_prepare_pytest_run_non_bare_uses_project_root(projectroot: Path) -> None:
	repo_dir = projectroot / "app"
	repo_dir.mkdir()
	repo = pygit2.init_repository(str(repo_dir), bare=False)
	_commit(repo, repo_dir)

	with patch.object(wt.settings, "PROJECTROOT", str(projectroot)):
		cwd, report = wt.prepare_pytest_run("app")

	assert cwd == repo_dir.resolve()
	assert report == (repo_dir / ".pygitweb" / "pytest_report.html").resolve()
	assert not (repo_dir / ".pygitweb" / "wt").exists()


def test_prepare_pytest_run_bare_creates_worktree(projectroot: Path) -> None:
	repo_dir = projectroot / "bare.git"
	repo_dir.mkdir()
	repo = pygit2.init_repository(str(repo_dir), bare=True)
	_commit(repo, repo_dir)

	with patch.object(wt.settings, "PROJECTROOT", str(projectroot)):
		cwd, report = wt.prepare_pytest_run("bare.git")
		cwd2, _ = wt.prepare_pytest_run("bare.git")

	assert cwd == (repo_dir / ".pygitweb" / "wt").resolve()
	assert cwd == cwd2
	assert (cwd / "README.md").read_text(encoding="utf-8") == "hello\n"
	assert report == (repo_dir / ".pygitweb" / "pytest_report.html").resolve()
	repo2 = pygit2.Repository(str(repo_dir))
	assert wt.WORKTREE_NAME in repo2.list_worktrees()
	assert Path(repo2.lookup_worktree(wt.WORKTREE_NAME).path).resolve() == cwd


def test_prepare_pytest_run_bare_resets_worktree_on_repeat(projectroot: Path) -> None:
	repo_dir = projectroot / "bare.git"
	repo_dir.mkdir()
	repo = pygit2.init_repository(str(repo_dir), bare=True)
	_commit(repo, repo_dir, branch="main")

	with patch.object(wt.settings, "PROJECTROOT", str(projectroot)):
		cwd, _ = wt.prepare_pytest_run("bare.git")
		(cwd / "dirty.txt").write_text("x", encoding="utf-8")
		wt.prepare_pytest_run("bare.git")

	assert not (cwd / "dirty.txt").exists()
	assert (cwd / "README.md").is_file()


def test_cleanup_pytest_run_removes_bare_worktree(projectroot: Path) -> None:
	repo_dir = projectroot / "bare.git"
	repo_dir.mkdir()
	repo = pygit2.init_repository(str(repo_dir), bare=True)
	_commit(repo, repo_dir)

	with patch.object(wt.settings, "PROJECTROOT", str(projectroot)):
		wt.prepare_pytest_run("bare.git")
		wt.cleanup_pytest_run("bare.git")

	repo2 = pygit2.Repository(str(repo_dir))
	assert wt.WORKTREE_NAME not in repo2.list_worktrees()
	assert not (repo_dir / ".pygitweb" / "wt").exists()


def test_cleanup_pytest_run_non_bare_noop(projectroot: Path) -> None:
	repo_dir = projectroot / "app"
	repo_dir.mkdir()
	repo = pygit2.init_repository(str(repo_dir), bare=False)
	_commit(repo, repo_dir)

	with patch.object(wt.settings, "PROJECTROOT", str(projectroot)):
		wt.cleanup_pytest_run("app")

	assert not (repo_dir / ".pygitweb" / "wt").exists()