"""Custom widgets for Fliiq TUI — Claude Code-style display."""

import ast
from pathlib import Path

from textual import events
from textual.app import ComposeResult
from textual.containers import Horizontal, ScrollableContainer
from textual.message import Message as TextualMessage
from textual.reactive import reactive
from textual.widgets import Collapsible, Markdown, OptionList, Static, TextArea

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _shorten_path(path: str, max_len: int = 50) -> str:
    """Shorten a file path for display, keeping the filename and some context."""
    if len(path) <= max_len:
        return path
    p = Path(path)
    parts = p.parts
    if len(parts) <= 2:
        return path
    return f".../{'/'.join(parts[-2:])}"


def _count_lines(text: str) -> int:
    """Count lines in a string."""
    if not text:
        return 0
    return text.count("\n") + (1 if not text.endswith("\n") else 0)


def _parse_tool_result(result: str) -> dict | str:
    """Try to parse a tool result as a Python dict."""
    try:
        parsed = ast.literal_eval(result)
        if isinstance(parsed, dict):
            return parsed
    except (ValueError, SyntaxError):
        pass
    return result


def _format_tool_summary(name: str, tool_input: dict, result: str) -> str:
    """Build a one-line summary for a tool call (collapsed view)."""
    parsed = _parse_tool_result(result)

    # File edits — show path + added/removed counts
    if name == "edit_file":
        path = _shorten_path(tool_input.get("path", ""))
        old = tool_input.get("old_text", "")
        new = tool_input.get("new_text", "")
        added = _count_lines(new)
        removed = _count_lines(old)
        return f"Update({path}) \u00b7 +{added} -{removed}"

    # File reads — show path
    if name == "read_file":
        path = _shorten_path(tool_input.get("path", ""))
        if isinstance(parsed, dict) and "content" in parsed:
            lines = _count_lines(str(parsed["content"]))
            return f"Read({path}) \u00b7 {lines} lines"
        return f"Read({path})"

    # File writes — show path + line count
    if name == "write_file":
        path = _shorten_path(tool_input.get("path", ""))
        content = tool_input.get("content", "")
        lines = _count_lines(content)
        return f"Write({path}) \u00b7 {lines} lines"

    # Shell commands — show command + exit code
    if name in ("shell", "shell_exec", "subprocess_exec"):
        cmd = tool_input.get("command", "")
        if len(cmd) > 60:
            cmd = cmd[:57] + "..."
        if isinstance(parsed, dict) and "exit_code" in parsed:
            code = parsed["exit_code"]
            return f"Bash({cmd}) \u00b7 exit {code}"
        return f"Bash({cmd})"

    # Grep/Find — show pattern or path
    if name == "grep":
        pattern = tool_input.get("pattern", "")
        return f"Grep({pattern[:40]})"

    if name == "find":
        pattern = tool_input.get("pattern", tool_input.get("name", ""))
        return f"Find({pattern[:40]})"

    if name == "list_directory":
        path = _shorten_path(tool_input.get("path", "."))
        return f"ListDir({path})"

    # Web tools
    if name == "web_search":
        query = tool_input.get("query", "")
        return f"WebSearch({query[:40]})"

    if name == "web_fetch":
        url = tool_input.get("url", "")
        if len(url) > 50:
            url = url[:47] + "..."
        return f"WebFetch({url})"

    # Default — tool_name(first_param_value)
    first_val = ""
    for v in tool_input.values():
        first_val = str(v)[:40]
        break
    if first_val:
        return f"{name}({first_val})"
    return name


def _format_tool_detail(name: str, tool_input: dict, result: str) -> str:
    """Build expanded detail text for a tool call (inside collapsible)."""
    parsed = _parse_tool_result(result)

    # File edits — show diff
    if name == "edit_file":
        old = tool_input.get("old_text", "")
        new = tool_input.get("new_text", "")
        lines = []
        for line in old.splitlines():
            lines.append(f"  - {line}")
        for line in new.splitlines():
            lines.append(f"  + {line}")
        return "\n".join(lines) if lines else str(result)[:500]

    # Shell — show stdout/stderr
    if name in ("shell", "shell_exec", "subprocess_exec") and isinstance(parsed, dict):
        parts = []
        if parsed.get("stdout", "").strip():
            parts.append(parsed["stdout"].strip()[:500])
        if parsed.get("stderr", "").strip():
            parts.append(f"stderr: {parsed['stderr'].strip()[:200]}")
        return "\n".join(parts) if parts else "(no output)"

    # File reads — show first few lines
    if name == "read_file" and isinstance(parsed, dict) and "content" in parsed:
        content = str(parsed["content"])
        lines = content.splitlines()[:20]
        if len(content.splitlines()) > 20:
            lines.append(f"  ... ({len(content.splitlines()) - 20} more lines)")
        return "\n".join(lines)

    # Default — truncated result
    return str(result)[:500]


# ---------------------------------------------------------------------------
# Welcome Screen
# ---------------------------------------------------------------------------

class WelcomeScreen(Static):
    """Split-panel welcome screen shown on startup."""

    DEFAULT_CSS = """
    WelcomeScreen {
        width: 100%;
        height: auto;
        margin: 1 2;
        padding: 1 2;
        border: round $primary;
    }
    """

    def __init__(self, version: str, cwd: str, model: str = "") -> None:
        lines = []
        lines.append(' \\(")/')
        lines.append(" -( )-")
        lines.append(" /(*)\\")
        lines.append("")
        lines.append(f"[bold]Fliiq v{version}[/bold]")
        lines.append("")
        if model:
            lines.append(f"[dim]{model}[/dim]")
        lines.append(f"[dim]{cwd}[/dim]")
        lines.append("")
        lines.append("[dim]\u2500\u2500\u2500[/dim]")
        lines.append("")
        lines.append("[bold]Tips[/bold]")
        lines.append("  /help \u2014 commands")
        lines.append("  /mode \u2014 cycle modes")
        lines.append("  shift+tab \u2014 quick mode switch")
        lines.append("  esc \u2014 cancel running agent")
        lines.append("  /copy \u2014 copy last response to clipboard")
        super().__init__("\n".join(lines))


# ---------------------------------------------------------------------------
# Messages
# ---------------------------------------------------------------------------

class UserMessage(Static):
    """User message with prompt prefix."""

    DEFAULT_CSS = """
    UserMessage {
        padding: 0 1;
        margin: 0 0 1 0;
        width: 100%;
        color: $primary;
    }
    """

    def __init__(self, content: str) -> None:
        # Escape brackets for Rich markup
        escaped = content.replace("[", "\\[")
        super().__init__(f"[bold]\u276f[/bold] {escaped}")


class AgentMessage(Static):
    """Agent message container with bullet prefix and markdown content."""

    DEFAULT_CSS = """
    AgentMessage {
        width: 100%;
        margin: 0 0 1 0;
        padding: 0 1;
    }

    AgentMessage .agent-prefix {
        width: auto;
        color: $text;
    }

    AgentMessage Markdown {
        margin: 0;
        padding: 0;
    }
    """

    def __init__(self, content: str) -> None:
        super().__init__()
        self._content = content

    def compose(self) -> ComposeResult:
        yield Static("\u23fa", classes="agent-prefix")
        yield Markdown(self._content)

    def update_content(self, content: str) -> None:
        """Update the markdown content (for streaming or updates)."""
        self._content = content
        try:
            md = self.query_one(Markdown)
            md.update(content)
        except Exception:
            pass


class CompletionMessage(Static):
    """Completion indicator with iteration count — lightweight, no heavy border."""

    DEFAULT_CSS = """
    CompletionMessage {
        padding: 0 1;
        margin: 0 0 1 0;
        width: 100%;
    }

    CompletionMessage .completion-prefix {
        color: $success;
    }

    CompletionMessage .completion-meta {
        color: $text-muted;
    }
    """

    def __init__(self, content: str, iterations: str = "") -> None:
        super().__init__()
        self._content = content
        self._iterations = iterations

    def compose(self) -> ComposeResult:
        yield Static("[bold green]\u23fa[/bold green]", classes="completion-prefix")
        yield Markdown(self._content)
        if self._iterations:
            yield Static(f"[dim]{self._iterations}[/dim]", classes="completion-meta")


# ---------------------------------------------------------------------------
# Tool Call Display
# ---------------------------------------------------------------------------

class ToolCallWidget(Static):
    """Single tool call with context-aware summary."""

    DEFAULT_CSS = """
    ToolCallWidget {
        padding: 0 1;
        width: 100%;
        color: $text-muted;
    }
    """

    def __init__(self, name: str, tool_input: dict, result: str, connector: str = "\u251c\u2500") -> None:
        summary = _format_tool_summary(name, tool_input, result)
        detail = _format_tool_detail(name, tool_input, result)
        detail_lines = [line for line in detail.splitlines() if line.strip()] if detail else []

        lines = [f"  {connector} {summary}"]
        if detail_lines:
            lines.append(f"     \u23bf  {detail_lines[0][:120]}")
            for dl in detail_lines[1:3]:
                lines.append(f"        {dl[:120]}")
            remaining = len(detail_lines) - 3
            if remaining > 0:
                lines.append(f"        ... +{remaining} more lines")

        super().__init__("\n".join(lines))
        self._name = name
        self._tool_input = tool_input
        self._result = result


class ToolCallGroup(Collapsible):
    """Collapsible group of consecutive tool calls with tree connectors."""

    DEFAULT_CSS = """
    ToolCallGroup {
        padding: 0;
        margin: 0 0 1 0;
        width: 100%;
    }

    ToolCallGroup CollapsibleTitle {
        color: $text-muted;
        padding: 0 1;
    }
    """

    def __init__(self) -> None:
        super().__init__(title="\u23fa 1 tool use (ctrl+o to expand)", collapsed=True)
        self._count = 0
        self._tools: list[tuple[str, dict, str]] = []

    def add_tool_call(self, name: str, tool_input: dict, result: str) -> None:
        """Add a tool call and update tree connectors."""
        self._tools.append((name, tool_input, result))
        self._count = len(self._tools)

        # Rebuild all connectors — last item gets └─, others get ├─
        for child in list(self.query(ToolCallWidget)):
            child.remove()

        for i, (n, ti, r) in enumerate(self._tools):
            is_last = i == len(self._tools) - 1
            connector = "\u2514\u2500" if is_last else "\u251c\u2500"
            widget = ToolCallWidget(n, ti, r, connector=connector)
            self.mount(widget)

        # Update title
        uses = "use" if self._count == 1 else "uses"
        self.title = f"\u23fa {self._count} tool {uses} (ctrl+o to expand)"


# ---------------------------------------------------------------------------
# Plan Approval
# ---------------------------------------------------------------------------

class PlanApprovalMessage(Static):
    """Styled plan summary — signals action required."""

    DEFAULT_CSS = """
    PlanApprovalMessage {
        padding: 1 2;
        margin: 1 0;
        width: 100%;
        border: round $warning;
        color: $text;
    }
    """

    def __init__(self, summary: str) -> None:
        escaped = summary.replace("[", "\\[")
        super().__init__(f"Plan Summary:\n\n{escaped}")


class PlanApprovalSelector(OptionList):
    """Arrow-key option list for plan approval next steps."""

    DEFAULT_CSS = """
    PlanApprovalSelector {
        height: auto;
        max-height: 8;
        margin: 0 0 1 0;
        width: 100%;
        border: round $warning;
    }
    """

    def __init__(self, options: list[dict], recommended: int = 1) -> None:
        self.option_data = options
        labels = []
        for i, opt in enumerate(options, 1):
            rec = " (recommended)" if i == recommended else ""
            labels.append(f"{opt['label']}{rec} \u2014 {opt['description']}")
        labels.append("Type your own response")
        super().__init__(*labels)
        if 1 <= recommended <= len(options):
            self.highlighted = recommended - 1


# ---------------------------------------------------------------------------
# Input
# ---------------------------------------------------------------------------

class FliiqInput(TextArea):
    """Multi-line input: Enter submits, Shift+Enter adds newline."""

    class Submitted(TextualMessage):
        """Fired when user presses Enter to submit."""

        def __init__(self, value: str) -> None:
            super().__init__()
            self.value = value

    DEFAULT_CSS = """
    FliiqInput {
        height: auto;
        min-height: 1;
        max-height: 5;
        width: 1fr;
        border: none;
        background: $background;
    }

    FliiqInput:focus {
        border: none;
    }
    """

    def __init__(self) -> None:
        super().__init__(language=None, show_line_numbers=False)
        self.tab_behavior = "focus"

    def _on_key(self, event: events.Key) -> None:
        """Enter = submit, Shift+Enter = newline."""
        if event.key == "enter":
            event.prevent_default()
            event.stop()
            text = self.text.strip()
            if text:
                self.clear()
                self.post_message(self.Submitted(text))


# ---------------------------------------------------------------------------
# Status Bar / Mode Input
# ---------------------------------------------------------------------------

class StatusBar(Static):
    """Bottom status bar: full-width input on top, info row below."""

    _MODE_COLORS = {
        "autonomous": "green",
        "supervised": "yellow",
        "plan": "red",
    }

    DEFAULT_CSS = """
    StatusBar {
        height: auto;
        min-height: 3;
        background: $surface;
        layout: vertical;
        padding: 0 1;
        border-top: solid $primary;
    }

    StatusBar .thinking-bar {
        display: none;
        width: 100%;
        height: 1;
        color: $warning;
    }

    StatusBar .input-row {
        width: 100%;
        height: auto;
        border-bottom: solid $primary;
    }

    StatusBar .input-prompt {
        width: 2;
        height: 1;
        color: $primary;
    }

    StatusBar FliiqInput {
        width: 1fr;
    }

    StatusBar .info-bar {
        width: 100%;
        height: 1;
        color: $text-muted;
    }
    """

    mode = reactive("autonomous")
    agent_running = reactive(False)

    def __init__(self, model_name: str = "", **kwargs) -> None:
        super().__init__(**kwargs)
        self._model_name = model_name

    def compose(self) -> ComposeResult:
        yield Static("", id="thinking-bar", classes="thinking-bar")
        with Horizontal(classes="input-row"):
            yield Static("\u276f ", classes="input-prompt")
            yield FliiqInput()
        yield Static(self._build_info_text(), id="info-bar", classes="info-bar")

    def show_thinking(self, elapsed: str = "0s") -> None:
        bar = self.query_one("#thinking-bar", Static)
        bar.update(f"\u23fa Thinking... {elapsed}")
        bar.styles.display = "block"

    def update_thinking(self, elapsed: str) -> None:
        bar = self.query_one("#thinking-bar", Static)
        bar.update(f"\u23fa Thinking... {elapsed}")

    def hide_thinking(self) -> None:
        bar = self.query_one("#thinking-bar", Static)
        bar.styles.display = "none"

    def _build_info_text(self) -> str:
        color = self._MODE_COLORS.get(self.mode, "white")
        parts = [f"[bold {color}]{self.mode}[/bold {color}] [dim](shift+tab)[/dim]"]
        if self._model_name:
            parts.append(f"[dim]{self._model_name}[/dim]")
        if self.agent_running:
            parts.append("[dim]esc: cancel[/dim]")
        return " \u00b7 ".join(parts)

    def _refresh_info(self) -> None:
        try:
            self.query_one("#info-bar", Static).update(self._build_info_text())
        except Exception:
            pass  # Not yet composed

    def watch_agent_running(self, _running: bool) -> None:
        self._refresh_info()

    def watch_mode(self, _new_mode: str) -> None:
        self._refresh_info()

    def focus_input(self) -> None:
        self.query_one(FliiqInput).focus()

    @property
    def input_widget(self) -> FliiqInput:
        return self.query_one(FliiqInput)

    def on_key(self, event: events.Key) -> None:
        if event.key == "shift+tab":
            self.app.action_cycle_mode()
            event.stop()


# ---------------------------------------------------------------------------
# Message Log
# ---------------------------------------------------------------------------

class MessageLog(ScrollableContainer):
    """Scrollable container for conversation messages."""

    DEFAULT_CSS = """
    MessageLog {
        height: 1fr;
        padding: 1;
        scrollbar-gutter: stable;
    }
    """

    def add_user_message(self, content: str) -> None:
        widget = UserMessage(content)
        self.mount(widget)
        self.scroll_end(animate=False)

    def add_agent_message(self, content: str) -> AgentMessage:
        widget = AgentMessage(content)
        self.mount(widget)
        self.scroll_end(animate=False)
        return widget

    def add_tool_call(self, name: str, tool_input: dict, result: str) -> None:
        """Add tool call to a collapsible group with context-aware display."""
        children = list(self.children)
        if children and isinstance(children[-1], ToolCallGroup):
            group = children[-1]
        else:
            group = ToolCallGroup()
            self.mount(group)
        group.add_tool_call(name, tool_input, result)
        self.scroll_end(animate=False)

    def add_completion(self, content: str, iterations: str = "") -> None:
        widget = CompletionMessage(content, iterations)
        self.mount(widget)
        self.scroll_end(animate=False)

    def add_plan_approval(self, summary: str) -> None:
        widget = PlanApprovalMessage(summary)
        self.mount(widget)
        self.scroll_end(animate=False)

    def add_system_message(self, content: str) -> None:
        """Add a system/info message (muted color)."""
        escaped = content.replace("[", "\\[")
        widget = Static(f"[dim]{escaped}[/dim]")
        widget.styles.padding = (0, 1)
        widget.styles.margin = (0, 0, 1, 0)
        self.mount(widget)
        self.scroll_end(animate=False)

    def clear_messages(self) -> None:
        for child in list(self.children):
            child.remove()
