from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pytest
from fastapi.testclient import TestClient
from pygit2 import Oid, Signature, init_repository

from pygittools.merge import MR_REF_PREFIX, MergeRequest
from pygittools.tasks import BOARD_REF_PREFIX, EMPTY_TREE_OID_HEX, TASK_REF_PREFIX, Board, Task
from pygitweb.board_refs import board_ref_manifest
from pygitweb.config import settings
from pygitweb.main import app

EMPTY_TREE_OID = Oid(hex=EMPTY_TREE_OID_HEX)
SIG = Signature("tester", "tester@example.com")


@pytest.fixture
def manifest_env(tmp_path: Path) -> dict[str, str | TestClient]:
	root = tmp_path / "manifest"
	repo_dir = root / "demo"
	repo_dir.mkdir(parents=True)
	repo = init_repository(str(repo_dir), bare=False)

	index = repo.index
	(repo_dir / "README").write_text("hello\n", encoding="utf-8")
	index.add("README")
	index.write()
	tree = index.write_tree()
	first = repo.create_commit("refs/heads/main", SIG, SIG, "Initial commit", tree, [])
	second_tree = index.write_tree()
	second = repo.create_commit("refs/heads/main", SIG, SIG, "Second commit", second_tree, [first])
	repo.set_head(second)

	board_ref = f"{BOARD_REF_PREFIX}Tasks"
	board = Board(EMPTY_TREE_OID, board_ref, tagger="", description="Main")
	task_ref = f"{TASK_REF_PREFIX}task_1782144988054"
	task = Task(
		EMPTY_TREE_OID,
		task_ref,
		tagger="",
		title="Ref lookup task",
		description="See @8054",
		status=Task.Status.TODO,
	)
	task_oid = task.write(repo)
	board.tasks = [str(task_oid)]
	board.update_message()
	board.write(repo)

	mr_ref = f"{MR_REF_PREFIX}aaaabbbbccccdddd"
	mr = MergeRequest(
		target=second,
		opener=SIG,
		ours="refs/heads/main",
		title="Test MR",
		description="Merge me",
		name=mr_ref,
	)
	mr.write(repo)

	with (
		patch.object(settings, "PROJECTROOT", str(root)),
		patch.object(settings, "PROJECTS_LIST", str(root)),
		patch.object(settings, "STRICT_EXPORT", False),
		patch.object(settings, "EXPORT_OK", ""),
		patch.object(settings, "AUTH", False),
	):
		yield {"client": TestClient(app), "project": "demo", "task_ref": task_ref, "mr_ref": mr_ref}


def test_board_ref_manifest_includes_commits_tasks_and_merge_requests(
	manifest_env: dict[str, str | TestClient],
) -> None:
	manifest = board_ref_manifest(str(manifest_env["project"]), "Tasks")

	assert len(manifest["commits"]) == 2
	assert "Second commit" in manifest["commits"][0]["name"]
	assert manifest["commits"][0]["insert"] == manifest["commits"][0]["name"].split()[0]

	task_entries = [entry for entry in manifest["refs"] if "1782144988054" in entry["tags"]]
	assert len(task_entries) == 1
	assert task_entries[0]["insert"] == "1782144988054"
	assert task_entries[0]["name"] == "Ref lookup task"
	assert "task=refs" in task_entries[0]["url"]
	assert "task_1782144988054" in task_entries[0]["url"]

	mr_entries = [entry for entry in manifest["refs"] if entry["insert"] == "aaaabbbbccccdddd"]
	assert len(mr_entries) == 1
	assert mr_entries[0]["name"] == "Test MR"
	assert "a=tag" in mr_entries[0]["url"]


def test_board_ref_manifest_route(manifest_env: dict[str, str | TestClient]) -> None:
	client = manifest_env["client"]
	assert isinstance(client, TestClient)
	resp = client.get(
		f"/project/{manifest_env['project']}/board-ref-manifest",
		params={"b": "Tasks"},
	)
	assert resp.status_code == 200
	body = resp.json()
	assert len(body["commits"]) == 2
	assert any(entry["insert"] == "1782144988054" for entry in body["refs"])