"""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, move_hint

_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:
        move = move_hint()
        if not self._entries:
            return f"No commits — {move}"
        entry = self._entries[self._list_state.selected or 0]
        return f"{entry.short_id} {entry.date} — {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")