"""Commit SHA tracker for incremental indexing.

Stores the last-indexed commit SHA per repo in a JSON file.
If the current HEAD matches the stored SHA, the repo is skipped.
Deleting the tracker file forces a full re-index of all repos.
"""

from __future__ import annotations

import json
import logging
import subprocess
from pathlib import Path

logger = logging.getLogger(__name__)

_TRACKER_FILENAME = "index_tracker.json"


class IndexTracker:
    """Tracks last-indexed commit SHA per repo to enable incremental indexing."""

    def __init__(self, data_dir: Path) -> None:
        self._path = data_dir / _TRACKER_FILENAME
        self._data: dict[str, str] = {}
        self._load()

    def _load(self) -> None:
        if self._path.exists():
            try:
                self._data = json.loads(self._path.read_text())
            except (json.JSONDecodeError, OSError):
                self._data = {}

    def _save(self) -> None:
        self._path.parent.mkdir(parents=True, exist_ok=True)
        self._path.write_text(json.dumps(self._data, indent=2))

    @staticmethod
    def _key(source: str, org: str, repo: str) -> str:
        return f"{source}/{org}/{repo}"

    @staticmethod
    def get_head_sha(repo_dir: Path) -> str | None:
        """Get the current HEAD commit SHA of a git repo."""
        try:
            result = subprocess.run(
                ["git", "rev-parse", "HEAD"],
                cwd=str(repo_dir),
                capture_output=True,
                text=True,
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except Exception:
            pass
        return None

    def get_stored_sha(self, source: str, org: str, repo: str) -> str | None:
        """Get the last-indexed SHA for a repo."""
        return self._data.get(self._key(source, org, repo))

    def needs_reindex(self, source: str, org: str, repo: str, repo_dir: Path) -> bool:
        """Check if a repo needs re-indexing by comparing HEAD SHA."""
        head = self.get_head_sha(repo_dir)
        if head is None:
            return True  # Can't determine — re-index to be safe
        stored = self.get_stored_sha(source, org, repo)
        if stored == head:
            logger.debug("Skipping %s/%s/%s — HEAD %s unchanged", source, org, repo, head[:8])
            return False
        return True

    def mark_indexed(self, source: str, org: str, repo: str, repo_dir: Path) -> None:
        """Record that a repo has been indexed at its current HEAD."""
        head = self.get_head_sha(repo_dir)
        if head:
            self._data[self._key(source, org, repo)] = head
            self._save()

    def remove(self, source: str, org: str, repo: str) -> None:
        """Remove a repo from the tracker (forces re-index on next run)."""
        key = self._key(source, org, repo)
        if key in self._data:
            del self._data[key]
            self._save()

    def clear(self) -> None:
        """Clear all tracked SHAs (forces full re-index)."""
        self._data = {}
        self._save()
