"""Working tree status and index helpers for the pgt TUI."""

from __future__ import annotations

import subprocess

from pygit2 import Commit, IndexEntry, Repository
from pygit2.enums import FileStatus

from pygittools.hooks_push import git_executable

_INDEX_FLAGS = (
	FileStatus.INDEX_NEW
	| FileStatus.INDEX_MODIFIED
	| FileStatus.INDEX_DELETED
	| FileStatus.INDEX_RENAMED
	| FileStatus.INDEX_TYPECHANGE
)
_WT_FLAGS = (
	FileStatus.WT_MODIFIED
	| FileStatus.WT_DELETED
	| FileStatus.WT_TYPECHANGE
	| FileStatus.WT_RENAMED
	| FileStatus.WT_UNREADABLE
)


def categorize_status(repo: Repository) -> tuple[list[str], list[str], list[str]]:
	"""Return staged, unstaged, and untracked paths from ``repo.status()``."""
	status = repo.status()
	staged = sorted(path for path, flags in status.items() if flags & _INDEX_FLAGS)
	unstaged = sorted(path for path, flags in status.items() if flags & _WT_FLAGS)
	untracked = sorted(path for path, flags in status.items() if flags & FileStatus.WT_NEW)
	return staged, unstaged, untracked


def stage_path(repo: Repository, path: str) -> None:
	index = repo.index
	index.add(path)
	index.write()


def stage_paths(repo: Repository, paths: list[str]) -> None:
	if not paths:
		return
	index = repo.index
	for path in paths:
		index.add(path)
	index.write()


def unstage_path(repo: Repository, path: str) -> None:
	index = repo.index
	head_tree = repo.head.peel(Commit).tree
	if path in head_tree:
		entry = head_tree[path]
		index.add(IndexEntry(path, entry.id, entry.filemode))
	else:
		index.remove(path)
	index.write()


def unstage_paths(repo: Repository, paths: list[str]) -> None:
	if not paths:
		return
	index = repo.index
	head_tree = repo.head.peel(Commit).tree
	for path in paths:
		if path in head_tree:
			entry = head_tree[path]
			index.add(IndexEntry(path, entry.id, entry.filemode))
		else:
			index.remove(path)
	index.write()


def commit_staged(repo: Repository, message: str) -> tuple[bool, str]:
	"""Create a commit via the git CLI so client hooks run. Returns (ok, error)."""
	trimmed = message.strip()
	if not trimmed:
		return False, "Commit message is empty"
	staged, _, _ = categorize_status(repo)
	if not staged:
		return False, "Nothing staged to commit"
	workdir = repo.workdir
	if workdir is None:
		return False, "Repository has no working directory"

	result = subprocess.run(
		[git_executable(), "commit", "-m", trimmed],
		cwd=workdir,
		capture_output=True,
		text=True,
		check=False,
	)
	if result.returncode == 0:
		return True, ""

	output = (result.stderr or result.stdout or "Commit failed").strip()
	if not output:
		return False, "Commit failed"
	return False, output.splitlines()[-1]