"""Git worktree listing and mutation via the git CLI."""

from __future__ import annotations

import subprocess
from dataclasses import dataclass
from pathlib import Path

from pygit2 import Repository

from pygittools.hooks_push import git_executable


@dataclass(frozen=True, slots=True)
class WorktreeInfo:
    path: Path
    head: str | None
    branch: str | None
    bare: bool
    detached: bool
    locked: str | None
    prunable: str | None


def list_worktrees(repo: Repository) -> tuple[list[WorktreeInfo], str | None]:
    workdir = repo.workdir
    if workdir is None:
        return [], "Repository has no working directory"
    result = subprocess.run(
        [git_executable(), "worktree", "list", "--porcelain"],
        cwd=workdir,
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode != 0:
        output = (result.stderr or result.stdout or "git worktree list failed").strip()
        return [], output.splitlines()[-1]
    return parse_worktree_porcelain(result.stdout), None


def parse_worktree_porcelain(text: str) -> list[WorktreeInfo]:
    entries: list[WorktreeInfo] = []
    block: list[str] = []
    for raw_line in text.splitlines():
        line = raw_line.strip()
        if not line:
            if block:
                entries.append(_block_to_info(block))
                block = []
            continue
        if line.startswith("worktree "):
            if block:
                entries.append(_block_to_info(block))
            block = [line]
        else:
            block.append(line)
    if block:
        entries.append(_block_to_info(block))
    return entries


def _block_to_info(lines: list[str]) -> WorktreeInfo:
    path = Path(lines[0].removeprefix("worktree ").strip())
    head: str | None = None
    branch: str | None = None
    bare = False
    detached = False
    locked: str | None = None
    prunable: str | None = None
    for line in lines[1:]:
        if line == "bare":
            bare = True
        elif line == "detached":
            detached = True
        elif line == "locked":
            locked = ""
        elif line.startswith("locked "):
            locked = line.removeprefix("locked ").strip()
        elif line == "prunable":
            prunable = ""
        elif line.startswith("prunable "):
            prunable = line.removeprefix("prunable ").strip()
        elif line.startswith("HEAD "):
            head = line.removeprefix("HEAD ").strip()
        elif line.startswith("branch "):
            ref = line.removeprefix("branch ").strip()
            branch = ref.removeprefix("refs/heads/") if ref.startswith("refs/heads/") else ref
    return WorktreeInfo(
        path=path,
        head=head,
        branch=branch,
        bare=bare,
        detached=detached,
        locked=locked,
        prunable=prunable,
    )


def add_worktree(repo: Repository, path: Path, branch: str | None) -> tuple[bool, str]:
    workdir = repo.workdir
    if workdir is None:
        return False, "Repository has no working directory"
    trimmed_branch = (branch or "").strip()
    target = _resolve_worktree_path(workdir, path)
    cmd = [git_executable(), "worktree", "add"]
    if trimmed_branch:
        local = {name for name in repo.branches.local}
        if trimmed_branch in local:
            cmd.extend([str(target), trimmed_branch])
        else:
            cmd.extend(["-b", trimmed_branch, str(target)])
    else:
        cmd.append(str(target))
    result = subprocess.run(
        cmd,
        cwd=workdir,
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode == 0:
        return True, ""
    output = (result.stderr or result.stdout or "git worktree add failed").strip()
    if not output:
        return False, "git worktree add failed"
    return False, output.splitlines()[-1]


def remove_worktree(repo: Repository, path: Path, *, force: bool) -> tuple[bool, str]:
    workdir = repo.workdir
    if workdir is None:
        return False, "Repository has no working directory"
    cmd = [git_executable(), "worktree", "remove"]
    if force:
        cmd.append("--force")
    cmd.append(str(path.resolve()))
    result = subprocess.run(
        cmd,
        cwd=workdir,
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode == 0:
        return True, ""
    output = (result.stderr or result.stdout or "git worktree remove failed").strip()
    if not output:
        return False, "git worktree remove failed"
    return False, output.splitlines()[-1]


def _resolve_worktree_path(workdir: str | Path, path: Path) -> Path:
    if path.is_absolute():
        return path
    return (Path(workdir) / path).resolve()