from __future__ import annotations

import subprocess
from pathlib import Path
from typing import Iterable


def get_changed_files(
    repo_path: str, base_ref: str, head_ref: str = "HEAD"
) -> list[Path]:
    args = [
        "git",
        "-C",
        repo_path,
        "diff",
        "--name-only",
        base_ref,
        head_ref,
    ]
    try:
        result = subprocess.run(
            args, check=True, capture_output=True, text=True
        )
    except subprocess.CalledProcessError as exc:
        raise RuntimeError(
            f"git diff failed: {exc.stderr.strip() or exc}"
        ) from exc

    files = []
    for line in result.stdout.splitlines():
        value = line.strip()
        if value:
            files.append(Path(repo_path) / value)
    return files


def filter_existing(paths: Iterable[Path]) -> list[Path]:
    return [path for path in paths if path.exists()]


def filter_removed(paths: Iterable[Path]) -> list[Path]:
    return [path for path in paths if not path.exists()]


def get_merge_base(
    repo_path: str, ref1: str, ref2: str = "HEAD"
) -> str | None:
    """Return merge-base commit of ref1 and ref2 (start of story range). None on error."""
    args = [
        "git",
        "-C",
        repo_path,
        "merge-base",
        ref1,
        ref2,
    ]
    try:
        result = subprocess.run(
            args,
            check=True,
            capture_output=True,
            text=True,
        )
        return result.stdout.strip() or None
    except subprocess.CalledProcessError:
        return None


def read_file_at_ref(
    repo_path: str, path: Path, ref: str
) -> str | None:
    """Read file content at a git ref. Returns None if path is missing at ref."""
    repo = Path(repo_path)
    rel = path.relative_to(repo) if path.is_absolute() else path
    rel_str = rel.as_posix()
    args = [
        "git",
        "-C",
        repo_path,
        "show",
        f"{ref}:{rel_str}",
    ]
    try:
        result = subprocess.run(
            args,
            check=True,
            capture_output=True,
            text=True,
        )
        return result.stdout
    except subprocess.CalledProcessError:
        return None
