import json
import subprocess
import sys
from pathlib import Path
from sys import stderr
from typing import Any

import pygit2

from distgit.hooks import HookResult, PreCommit

"""
Pre-commit hook: run Ruff lint and format checks with machine-readable JSON on stdout.

Uses ``ruff check --output-format=json`` and
``ruff format --check --preview --output-format=json`` (preview is required for JSON on ``format``).
Parses JSON and prints a short summary to stderr on failure.
"""


def _ruff_base() -> list[str]:
	return [sys.executable, "-m", "ruff"]


def _parse_diagnostic_list(raw: str) -> list[dict[str, Any]]:
	s = raw.strip()
	if not s:
		return []
	data = json.loads(s)
	if isinstance(data, list):
		return [x for x in data if isinstance(x, dict)]
	return []


def _format_location(loc: object) -> str:
	if not isinstance(loc, dict):
		return "?:?"
	row = loc.get("row", "?")
	col = loc.get("column", "?")
	return f"{row}:{col}"


def _summarize_diagnostics(label: str, rows: list[dict[str, Any]]) -> str:
	lines = [f"{label}:"]
	for row in rows:
		fn = row.get("filename", "?")
		msg = row.get("message", row.get("code", "?"))
		loc = row.get("location")
		lines.append(f"  {fn}:{_format_location(loc)}: {msg}")
	return "\n".join(lines)


def _run_ruff_json(
	args: list[str],
	cwd: Path,
) -> tuple[int, list[dict[str, Any]], str]:
	proc = subprocess.run(
		_ruff_base() + args,
		cwd=cwd,
		capture_output=True,
		text=True,
	)
	stderr_extra = proc.stderr or ""
	try:
		rows = _parse_diagnostic_list(proc.stdout)
	except json.JSONDecodeError:
		return (
			1,
			[],
			f"invalid JSON from ruff (stdout):\n{proc.stdout!r}\nstderr:\n{stderr_extra}",
		)
	return proc.returncode, rows, stderr_extra


class PreCommitRuff(PreCommit):
	def __init__(self, repo: pygit2.Repository, paths: list[str] | None = None):
		super().__init__(repo)
		self.paths = paths if paths is not None else ["."]

	def run(self) -> HookResult:
		workdir = self.repo.workdir
		if workdir is None:
			stderr.write("PreCommitRuff: bare repository has no working tree; skipping ruff.\n")
			return HookResult.SUCCESS
		root = Path(workdir)

		rc, rows, extra = _run_ruff_json(["check", "--output-format=json", *self.paths], root)
		if rc != 0:
			stderr.write(_summarize_diagnostics("ruff check", rows) + "\n")
			if extra.strip():
				stderr.write(extra)
			return HookResult.FAILURE

		rc, rows, extra = _run_ruff_json(
			["format", "--check", "--preview", "--output-format=json", *self.paths],
			root,
		)
		if rc != 0:
			stderr.write(_summarize_diagnostics("ruff format", rows) + "\n")
			if extra.strip():
				stderr.write(extra)
			return HookResult.FAILURE

		return HookResult.SUCCESS