"""Tool-specific approval widgets for HITL display."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from textual.containers import Vertical
from textual.widgets import Markdown, Static

if TYPE_CHECKING:
    from textual.app import ComposeResult

# Constants for display limits
_MAX_VALUE_LEN = 200
_MAX_LINES = 30
_MAX_DIFF_LINES = 50
_MAX_PREVIEW_LINES = 20


def _escape_markup(text: str) -> str:
    """Escape Rich markup characters in text."""
    return text.replace("[", r"\[").replace("]", r"\]")


class ToolApprovalWidget(Vertical):
    """Base class for tool approval widgets."""

    def __init__(self, data: dict[str, Any]) -> None:
        """Initialize the tool approval widget with data."""
        super().__init__(classes="tool-approval-widget")
        self.data = data

    def compose(self) -> ComposeResult:
        """Default compose - override in subclasses."""
        yield Static("Tool details not available", classes="approval-description")


class GenericApprovalWidget(ToolApprovalWidget):
    """Generic approval widget for unknown tools."""

    def compose(self) -> ComposeResult:
        """Compose the generic tool display."""
        for key, value in self.data.items():
            if value is None:
                continue
            value_str = str(value)
            if len(value_str) > _MAX_VALUE_LEN:
                hidden = len(value_str) - _MAX_VALUE_LEN
                value_str = value_str[:_MAX_VALUE_LEN] + f"... ({hidden} more chars)"
            yield Static(f"{key}: {value_str}", markup=False, classes="approval-description")


class WriteFileApprovalWidget(ToolApprovalWidget):
    """Approval widget for write_file - shows diff or content with syntax highlighting."""

    def compose(self) -> ComposeResult:
        """Compose the file content display, showing diff if overwriting."""
        from ieum.widgets.diff import CollapsibleDiff

        file_path = self.data.get("file_path", "")
        content = self.data.get("content", "")
        file_extension = self.data.get("file_extension", "text")
        full_diff = self.data.get("full_diff", "")
        is_overwrite = self.data.get("is_overwrite", False)

        # File path header
        action = "Overwrite" if is_overwrite else "Create"
        yield Static(f"[bold cyan]{action}:[/bold cyan] {file_path}")

        # Show diff if overwriting an existing file
        if full_diff:
            yield CollapsibleDiff(
                full_diff, title=file_path, max_lines=_MAX_LINES, start_expanded=True,
            )
            return

        # New file: show content with syntax highlighting
        lines = content.split("\n")
        total_lines = len(lines)

        if total_lines > _MAX_LINES:
            shown_lines = lines[:_MAX_LINES]
            remaining = total_lines - _MAX_LINES
            truncated_content = "\n".join(shown_lines) + f"\n... ({remaining} more lines)"
            yield Markdown(f"```{file_extension}\n{truncated_content}\n```")
        else:
            yield Markdown(f"```{file_extension}\n{content}\n```")


class EditFileApprovalWidget(ToolApprovalWidget):
    """Approval widget for edit_file - shows enhanced diff with line numbers."""

    def compose(self) -> ComposeResult:
        """Compose the diff display using format_diff_textual for rich rendering."""
        from ieum.widgets.diff import CollapsibleDiff, format_diff_textual

        file_path = self.data.get("file_path", "")
        diff_lines = self.data.get("diff_lines", [])
        old_string = self.data.get("old_string", "")
        new_string = self.data.get("new_string", "")
        # Full-file diff from build_approval_preview (if available)
        full_diff = self.data.get("full_diff", "")

        # File path header
        yield Static(f"[bold cyan]File:[/bold cyan] {file_path}")

        if full_diff:
            # Use the full-file context diff with collapsible display
            yield CollapsibleDiff(
                full_diff, title=file_path, max_lines=_MAX_DIFF_LINES, start_expanded=True,
            )
        elif diff_lines:
            # Build a unified diff string from lines for format_diff_textual
            diff_text = "\n".join(diff_lines)
            rendered = format_diff_textual(diff_text, max_lines=_MAX_DIFF_LINES)
            yield Static(rendered)
        elif old_string or new_string:
            yield from self._render_strings_only(old_string, new_string)
        else:
            yield Static("No changes to display", classes="approval-description")

    def _render_strings_only(self, old_string: str, new_string: str) -> ComposeResult:
        """Render old/new strings as fallback."""
        if old_string:
            yield Static("[bold red]Removing:[/bold red]")
            yield from self._render_string_lines(old_string, is_addition=False)
            yield Static("")

        if new_string:
            yield Static("[bold green]Adding:[/bold green]")
            yield from self._render_string_lines(new_string, is_addition=True)

    def _render_string_lines(self, text: str, *, is_addition: bool) -> ComposeResult:
        """Render lines from a string with appropriate styling."""
        lines = text.split("\n")
        style = "[on #1e4620][#8ce99a]+" if is_addition else "[on #4a2020][#ff8787]-"
        end_style = "[/#8ce99a][/on #1e4620]" if is_addition else "[/#ff8787][/on #4a2020]"

        for line in lines[:_MAX_PREVIEW_LINES]:
            escaped = _escape_markup(line)
            yield Static(f"{style} {escaped}{end_style}")

        if len(lines) > _MAX_PREVIEW_LINES:
            remaining = len(lines) - _MAX_PREVIEW_LINES
            yield Static(f"[dim]... ({remaining} more lines)[/dim]")
