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"])