"""Scrollable commit log for the pgt TUI."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import UTC, datetime

from pygit2 import Commit, GitError, Repository
from pygit2.enums import SortMode
from pyratatui import Frame, List, ListItem, ListState, Rect

from pygittools.tui.git_colors import current_theme, log_entry_styles
from pygittools.tui.keys import KeyLike, is_down, is_up

_MAX_COMMITS = 500


@dataclass(frozen=True, slots=True)
class _LogEntry:
	short_id: str
	date: str
	subject: str


@dataclass(frozen=True, slots=True)
class LogNavKeyResult:
	layout_changed: bool = False


class CommitLogList:
	def __init__(self) -> None:
		self._entries: list[_LogEntry] = []
		self._list_state = ListState()

	def is_at_top(self) -> bool:
		return (self._list_state.selected or 0) == 0

	def refresh(self, repo: Repository) -> None:
		self._entries = _load_entries(repo)
		selected = self._list_state.selected or 0
		self._list_state.select(min(selected, max(0, len(self._entries) - 1)))

	def draw(self, frame: Frame, area: Rect, *, highlight: bool) -> None:
		if area.height <= 0 or area.width <= 0:
			return
		theme = current_theme()
		if not self._entries:
			frame.render_stateful_list(
				List([ListItem("No commits", theme.log_date)]),
				area,
				self._list_state,
			)
			return
		selected = self._list_state.selected or 0
		items: list[ListItem] = []
		for index, entry in enumerate(self._entries):
			focused = highlight and index == selected
			hash_style, date_style, subject_style = log_entry_styles(
				focused=focused,
				highlight=highlight,
			)
			del date_style, subject_style
			label = f"{entry.short_id} {entry.date} {entry.subject}"
			items.append(ListItem(label, hash_style if focused else theme.log_subject))
		frame.render_stateful_list(
			List(items).highlight_style(theme.focus).highlight_symbol(""),
			area,
			self._list_state,
		)

	def handle_nav_key(self, key: KeyLike, repo: Repository) -> LogNavKeyResult:
		del repo
		if is_up(key):
			selected = self._list_state.selected or 0
			self._list_state.select(max(0, selected - 1))
			return LogNavKeyResult()
		if is_down(key):
			selected = self._list_state.selected or 0
			self._list_state.select(min(max(0, len(self._entries) - 1), selected + 1))
			return LogNavKeyResult()
		return LogNavKeyResult()

	def status_hint(self) -> str:
		if not self._entries:
			return "No commits — j/k move"
		entry = self._entries[self._list_state.selected or 0]
		return f"{entry.short_id} {entry.date} — j/k move"


def _load_entries(repo: Repository) -> list[_LogEntry]:
	try:
		head = repo.head.target
	except GitError:
		return []
	entries: list[_LogEntry] = []
	for commit in repo.walk(head, SortMode.TIME):
		if not isinstance(commit, Commit):
			continue
		subject = commit.message.splitlines()[0] if commit.message else "(no message)"
		entries.append(
			_LogEntry(
				short_id=str(commit.id)[:7],
				date=_format_commit_date(commit),
				subject=subject,
			),
		)
		if len(entries) >= _MAX_COMMITS:
			break
	return entries


def _format_commit_date(commit: Commit) -> str:
	return datetime.fromtimestamp(commit.commit_time, tz=UTC).strftime("%Y-%m-%d")