"""PermissionsScreen widget for displaying tool permission requests with diff views.

Shows a split-pane layout: a file list on the left and a diff view on the right.
Supports three diff display modes (unified, side-by-side, context) toggled with ``d``.
Allow/reject keyboard bindings: ``a`` allow-once, ``A`` allow-always,
``r`` reject-once, ``R`` reject-always.
"""

from __future__ import annotations

import importlib
from typing import Any

from cleveragents.tui.permissions.models import (
    DiffDisplayMode,
    PermissionDecision,
    PermissionRequest,
    ToolPermissionRequest,
)

__all__ = ["PermissionsScreen"]

# ── Optional Textual import gate ─────────────────────────────────


def _load_static_base() -> type[Any]:
    try:
        return importlib.import_module("textual.widgets").Static
    except Exception:  # pragma: no cover

        class _FallbackStatic:
            def __init__(self, *args: object, **kwargs: object) -> None:
                self._text = ""

            def update(self, text: str) -> None:
                self._text = text

        return _FallbackStatic


_StaticBase = _load_static_base()

# ── Diff display mode cycle ───────────────────────────────────────

_DIFF_MODE_CYCLE: list[DiffDisplayMode] = [
    DiffDisplayMode.UNIFIED,
    DiffDisplayMode.SIDE_BY_SIDE,
    DiffDisplayMode.CONTEXT,
]


def _next_diff_mode(current: DiffDisplayMode) -> DiffDisplayMode:
    """Return the next diff display mode in the cycle."""
    idx = _DIFF_MODE_CYCLE.index(current)
    return _DIFF_MODE_CYCLE[(idx + 1) % len(_DIFF_MODE_CYCLE)]


# ── Rendering helpers ─────────────────────────────────────────────


def _render_file_list(
    changes: list[PermissionRequest],
    selected_index: int,
) -> str:
    """Render the file list panel."""
    lines: list[str] = [f"Files ({len(changes)} changes):"]
    for i, change in enumerate(changes):
        prefix = "❯ " if i == selected_index else "  "  # noqa: RUF001
        lines.append(f"{prefix}{change.path} [{change.change_type}]")
    return "\n".join(lines)


def _render_diff_panel(
    change: PermissionRequest | None,
    mode: DiffDisplayMode,
) -> str:
    """Render the diff panel for the selected file."""
    if change is None:
        return "(no file selected)"
    header = f"{change.path}\n{'─' * 50}"
    diff_text = change.render_diff(mode)
    if not diff_text:
        diff_text = "(no changes)"
    return f"{header}\n{diff_text}"


def _render_status_bar(
    tool_name: str,
    resource_name: str,
    mode: DiffDisplayMode,
) -> str:
    """Render the status bar at the bottom of the screen."""
    return (
        f"{tool_name} wants to modify files in {resource_name}\n"
        f"[M] Modified  [A] Added  [D] Deleted  |  Diff: {mode}\n"
        "a Allow │ A Allow Always │ r Reject │ R Reject Always │ j/k Nav │ d Diff │ esc"
    )


def _render_screen(
    request: ToolPermissionRequest,
    selected_index: int,
    diff_mode: DiffDisplayMode,
) -> str:
    """Render the full PermissionsScreen content as text."""
    file_list = _render_file_list(request.changes, selected_index)
    selected_change = request.changes[selected_index] if request.changes else None
    diff_panel = _render_diff_panel(selected_change, diff_mode)
    status_bar = _render_status_bar(request.tool_name, request.resource_name, diff_mode)
    return f"Permission Request\n\n{file_list}\n\n{diff_panel}\n\n{status_bar}"


# ── PermissionsScreen widget ──────────────────────────────────────


class PermissionsScreen(_StaticBase):
    """TUI widget that displays a tool permission request with a diff view.

    Layout:
    - Title: "Permission Request"
    - Left panel: file list with change type indicators
    - Right panel: diff view for the selected file
    - Status bar: keyboard bindings and diff mode indicator

    Keyboard bindings:
    - ``a``: allow once
    - ``A``: allow always (session)
    - ``r``: reject once
    - ``R``: reject always (session)
    - ``j`` / ``k``: navigate file list (next / previous)
    - ``d``: cycle diff display mode (unified → side-by-side → context)
    - ``escape``: dismiss (caller responsibility)
    """

    def __init__(self, *args: object, **kwargs: object) -> None:
        super().__init__(*args, **kwargs)
        self._request: ToolPermissionRequest | None = None
        self._selected_index: int = 0
        self._diff_mode: DiffDisplayMode = DiffDisplayMode.UNIFIED
        self._decision: PermissionDecision | None = None
        self._text: str = "(no permission request)"
        self.update(self._text)

    # ── Public API ────────────────────────────────────────────────

    @property
    def current_request(self) -> ToolPermissionRequest | None:
        """Return the currently displayed permission request."""
        return self._request

    @property
    def selected_index(self) -> int:
        """Return the index of the currently selected file."""
        return self._selected_index

    @property
    def diff_mode(self) -> DiffDisplayMode:
        """Return the current diff display mode."""
        return self._diff_mode

    @property
    def decision(self) -> PermissionDecision | None:
        """Return the decision made by the user, or None if pending."""
        return self._decision

    def load_request(self, request: ToolPermissionRequest) -> None:
        """Load a new permission request and reset state."""
        self._request = request
        self._selected_index = 0
        self._diff_mode = DiffDisplayMode.UNIFIED
        self._decision = None
        self._refresh()

    def clear(self) -> None:
        """Clear the current request and reset state."""
        self._request = None
        self._selected_index = 0
        self._diff_mode = DiffDisplayMode.UNIFIED
        self._decision = None
        self._text = ""
        self.update("")

    # ── Navigation ────────────────────────────────────────────────

    def navigate_next(self) -> None:
        """Move selection to the next file (``j`` key)."""
        if self._request and self._request.changes:
            self._selected_index = (self._selected_index + 1) % len(
                self._request.changes
            )
            self._refresh()

    def navigate_prev(self) -> None:
        """Move selection to the previous file (``k`` key)."""
        if self._request and self._request.changes:
            self._selected_index = (self._selected_index - 1) % len(
                self._request.changes
            )
            self._refresh()

    def cycle_diff_mode(self) -> DiffDisplayMode:
        """Cycle to the next diff display mode (``d`` key).

        Returns the new mode.
        """
        self._diff_mode = _next_diff_mode(self._diff_mode)
        self._refresh()
        return self._diff_mode

    def set_diff_mode(self, mode: DiffDisplayMode) -> None:
        """Set the diff display mode directly."""
        self._diff_mode = mode
        self._refresh()

    # ── Decision actions ──────────────────────────────────────────

    def allow_once(self) -> PermissionDecision:
        """Record an allow-once decision (``a`` key)."""
        return self._record_decision(PermissionDecision.ALLOW_ONCE)

    def allow_always(self) -> PermissionDecision:
        """Record an allow-always decision (``A`` key)."""
        return self._record_decision(PermissionDecision.ALLOW_ALWAYS)

    def reject_once(self) -> PermissionDecision:
        """Record a reject-once decision (``r`` key)."""
        return self._record_decision(PermissionDecision.REJECT_ONCE)

    def reject_always(self) -> PermissionDecision:
        """Record a reject-always decision (``R`` key)."""
        return self._record_decision(PermissionDecision.REJECT_ALWAYS)

    def _record_decision(self, decision: PermissionDecision) -> PermissionDecision:
        """Store the decision and update the display."""
        self._decision = decision
        if self._request is not None:
            self._request = self._request.apply_decision(decision)
        self._refresh()
        return decision

    # ── Rendering ─────────────────────────────────────────────────

    def _refresh(self) -> None:
        """Re-render the screen content."""
        if self._request is None:
            self._text = "(no permission request)"
            self.update(self._text)
            return
        self._text = _render_screen(
            self._request,
            self._selected_index,
            self._diff_mode,
        )
        self.update(self._text)
