from __future__ import annotations

from pathlib import Path

import pytest
from pygit2 import Repository, Signature, init_repository

from pygittools.tui.commit_draft import (
    commit_editmsg_path,
    load_commit_draft,
    save_commit_draft,
)

SIG = Signature("alice", "alice@example.com")


@pytest.fixture
def repo(tmp_path: Path) -> Repository:
    repo_path = tmp_path / "repo"
    repo_path.mkdir()
    return init_repository(str(repo_path), bare=False)


def test_load_returns_empty_when_missing(tmp_path: Path) -> None:
    assert load_commit_draft(tmp_path) == ""


def test_roundtrip_single_line_message(tmp_path: Path) -> None:
    save_commit_draft(tmp_path, "feat: add draft persistence")
    assert load_commit_draft(tmp_path) == "feat: add draft persistence"
    assert commit_editmsg_path(tmp_path).read_text(encoding="utf-8") == "feat: add draft persistence\n"


def test_load_ignores_git_comment_lines(tmp_path: Path) -> None:
    commit_editmsg_path(tmp_path).write_text(
        "feat: keep me\n\n# Please enter the commit message\n",
        encoding="utf-8",
    )
    assert load_commit_draft(tmp_path) == "feat: keep me"


def test_save_empty_removes_existing_file(tmp_path: Path) -> None:
    save_commit_draft(tmp_path, "wip")
    path = commit_editmsg_path(tmp_path)
    assert path.is_file()

    save_commit_draft(tmp_path, "")
    assert not path.exists()


def test_load_returns_empty_when_draft_matches_head(repo: Repository) -> None:
    tree = repo.index.write_tree()
    repo.create_commit("HEAD", SIG, SIG, "feat: shipped", tree, [])
    save_commit_draft(Path(repo.path), "feat: shipped")

    assert load_commit_draft(Path(repo.path), repo) == ""


def test_load_returns_draft_when_it_differs_from_head(repo: Repository) -> None:
    tree = repo.index.write_tree()
    repo.create_commit("HEAD", SIG, SIG, "feat: shipped", tree, [])
    save_commit_draft(Path(repo.path), "feat: next change")

    assert load_commit_draft(Path(repo.path), repo) == "feat: next change"


def test_read_draft_ignores_unreadable_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    path = commit_editmsg_path(tmp_path)
    path.write_text("draft\n", encoding="utf-8")

    def fail_read_text(self: Path, encoding: str = "utf-8") -> str:
        raise OSError("nope")

    monkeypatch.setattr(Path, "read_text", fail_read_text)
    assert load_commit_draft(tmp_path) == ""


def test_load_returns_draft_when_head_unavailable(repo: Repository) -> None:
    save_commit_draft(Path(repo.path), "wip")
    assert load_commit_draft(Path(repo.path), repo) == "wip"


def test_save_commit_draft_ignores_os_error(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    def fail_write_text(self: Path, data: str, encoding: str = "utf-8") -> None:
        raise OSError("disk full")

    monkeypatch.setattr(Path, "write_text", fail_write_text)
    save_commit_draft(tmp_path, "ignored")