"""Agent tool editor widget for configuring a single tool instance.

Provides a compound widget for editing tool configuration including:
- Tool selection
- Alias (defaults to tool_id)
- Config mode: none, inline JSON, or saved config reference
- Inline config JSON textarea with parameter_schema display
- Tool config picker for referencing saved configs
- Enable/disable toggle
- LLM instruction input
"""

import json
from datetime import datetime, timezone
from typing import Any
from uuid import UUID

from textual import on, work
from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical
from textual.message import Message
from textual.widget import Widget
from textual.widgets import (
    Button,
    Checkbox,
    Input,
    RadioButton,
    RadioSet,
    Static,
    TextArea,
)

from alloy_runtime_sdk.api_client.client import ApiClient
from alloy_runtime_types.dtos.agents import AgentToolConfig, AgentToolResponse
from alloy_runtime_types.dtos.tool_configs import ToolConfigSummary
from alloy_runtime_types.dtos.tools import ToolResponse

from cli.infrastructure.forms.tool_config_picker import ToolConfigPicker
from cli.infrastructure.forms.tool_picker import ToolPicker


def resolve_agent_tool_mode(tool: AgentToolResponse) -> str:
    tool_config_id = getattr(tool, "tool_config_id", None)
    config_source = getattr(tool, "config_source", None)
    submitted_config = getattr(tool, "submitted_config", None)
    if tool_config_id is not None or config_source == "tool_config":
        return "reference"
    if submitted_config is not None:
        return "inline"
    return "none"


class AgentToolEditor(Widget):
    """Editor widget for a single agent tool configuration.

    Provides UI for configuring a tool instance with support for:
    - Tool selection from available tools
    - Custom alias (enables multiple instances of same tool)
    - Three config modes: none, inline JSON, saved config reference
    - LLM instruction for per-agent hints

    Usage:
        editor = AgentToolEditor(client=client, id="tool-editor-1")
        # Get the configured tool
        config = editor.get_tool_config()  # AgentToolConfig | None
    """

    DEFAULT_CLASSES = "tool-editor"

    class ToolChanged(Message):
        """Posted when tool configuration changes."""

        def __init__(self, editor: "AgentToolEditor") -> None:
            self.editor = editor
            super().__init__()

    class RemoveRequested(Message):
        """Posted when user requests removal of this editor."""

        def __init__(self, editor: "AgentToolEditor") -> None:
            self.editor = editor
            super().__init__()

    def __init__(
        self,
        client: ApiClient,
        tool_order: int = 0,
        **kwargs: Any,
    ) -> None:
        """Initialize the agent tool editor.

        Args:
            client: ApiClient instance for API calls
            tool_order: Order of this tool in the agent's tool list
        """
        super().__init__(**kwargs)
        self._client = client
        self._tool_order = tool_order
        self._selected_tool: ToolResponse | None = None

    def compose(self) -> ComposeResult:
        with Vertical(classes="tool-editor-content"):
            # Header with title and remove button
            with Horizontal(classes="tool-header"):
                yield Static(
                    f"Tool #{self._tool_order + 1}",
                    classes="tool-title",
                    id="tool-title",
                )
                yield Button("Remove", id="remove-btn", variant="error")

            # Tool selection
            yield Static("Tool *", classes="field-label")
            yield ToolPicker(client=self._client, id="tool-picker")

            # Alias
            yield Static("Alias (defaults to tool ID)", classes="field-label")
            yield Input(placeholder="e.g., rag-testimonials", id="alias-input")

            # Config mode selection
            yield Static("Configuration", classes="field-label")
            with RadioSet(id="config-mode-radios"):
                yield RadioButton("No configuration", id="mode-none", value=True)
                yield RadioButton("Inline config (JSON)", id="mode-inline")
                yield RadioButton("Use saved config", id="mode-reference")

            # Inline config section (hidden by default)
            with Vertical(id="inline-config-section", classes="config-section hidden"):
                yield Static(
                    "Parameter Schema (reference):", classes="field-label schema-label"
                )
                yield TextArea(id="schema-display", read_only=True)
                yield Static("Config (JSON):", classes="field-label")
                yield TextArea(id="config-input", language="json")

            # Tool config reference section (hidden by default)
            with Vertical(
                id="config-reference-section", classes="config-section hidden"
            ):
                yield Static("Select saved config:", classes="field-label")
                yield ToolConfigPicker(
                    client=self._client,
                    tool_id=None,
                    id="tool-config-picker",
                )

            # Enable toggle
            yield Checkbox("Enabled", value=True, id="enabled-checkbox")

            # LLM instruction
            yield Static("LLM Instruction (optional)", classes="field-label")
            yield TextArea(
                id="llm-instruction-input",
            )

    @on(Button.Pressed, "#remove-btn")
    def on_remove_pressed(self, event: Button.Pressed) -> None:
        """Handle remove button press."""
        event.stop()
        self.post_message(self.RemoveRequested(self))

    @on(ToolPicker.SelectionChanged)
    def on_tool_selected(self, event: ToolPicker.SelectionChanged) -> None:
        """Handle tool selection change."""
        event.stop()
        self._selected_tool = event.selected_item

        # Update alias placeholder
        alias_input = self.query_one("#alias-input", Input)
        if self._selected_tool:
            alias_input.placeholder = f"Default: {self._selected_tool.id}"
        else:
            alias_input.placeholder = "e.g., rag-testimonials"

        # Update schema display
        self._update_schema_display()

        # Update tool config picker filter
        config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
        config_picker.tool_id = self._selected_tool.id if self._selected_tool else None

        # Post change event
        self.post_message(self.ToolChanged(self))

    @on(RadioSet.Changed, "#config-mode-radios")
    def on_config_mode_changed(self, event: RadioSet.Changed) -> None:
        """Handle config mode change."""
        event.stop()
        self._update_config_sections()
        self.post_message(self.ToolChanged(self))

    def _update_config_sections(self) -> None:
        """Show/hide config sections based on selected mode."""
        radios = self.query_one("#config-mode-radios", RadioSet)
        pressed = radios.pressed_button

        inline_section = self.query_one("#inline-config-section", Vertical)
        reference_section = self.query_one("#config-reference-section", Vertical)

        if pressed and pressed.id == "mode-inline":
            inline_section.remove_class("hidden")
            reference_section.add_class("hidden")
        elif pressed and pressed.id == "mode-reference":
            inline_section.add_class("hidden")
            reference_section.remove_class("hidden")
        else:
            inline_section.add_class("hidden")
            reference_section.add_class("hidden")

    def _update_schema_display(self) -> None:
        """Update the schema display with selected tool's parameter_schema."""
        schema_display = self.query_one("#schema-display", TextArea)

        if self._selected_tool and self._selected_tool.parameter_schema:
            schema_json = json.dumps(self._selected_tool.parameter_schema, indent=2)
            schema_display.text = schema_json
        else:
            schema_display.text = "(This tool has no configurable parameters)"

    def get_tool_config(self) -> AgentToolConfig | None:
        """Get the current tool configuration.

        Returns:
            AgentToolConfig if a tool is selected and valid, None otherwise
        """
        tool_picker = self.query_one("#tool-picker", ToolPicker)
        if not tool_picker.selected_tool:
            return None

        tool_id = tool_picker.selected_tool.id
        alias_input = self.query_one("#alias-input", Input)
        alias = alias_input.value.strip() or None

        # Get config mode
        radios = self.query_one("#config-mode-radios", RadioSet)
        pressed = radios.pressed_button
        config_mode = pressed.id if pressed else "mode-none"

        config: dict[str, Any] | None = None
        tool_config_id: UUID | None = None

        if config_mode == "mode-inline":
            config_input = self.query_one("#config-input", TextArea)
            config_text = config_input.text.strip()
            if config_text:
                try:
                    config = json.loads(config_text)
                except json.JSONDecodeError:
                    # Invalid JSON, will be caught by validation
                    config = None
        elif config_mode == "mode-reference":
            config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
            if config_picker.selected_config:
                tool_config_id = config_picker.selected_config.id

        enabled_checkbox = self.query_one("#enabled-checkbox", Checkbox)
        llm_instruction_input = self.query_one("#llm-instruction-input", TextArea)
        llm_instruction = llm_instruction_input.text.strip() or None

        return AgentToolConfig(
            tool_id=tool_id,
            tool_alias=alias,
            config=config,
            tool_config_id=tool_config_id,
            is_enabled=enabled_checkbox.value,
            tool_order=self._tool_order,
            llm_instruction=llm_instruction,
        )

    def validate(self) -> str | None:
        """Validate the current configuration.

        Returns:
            Error message if invalid, None if valid
        """
        tool_picker = self.query_one("#tool-picker", ToolPicker)
        if not tool_picker.selected_tool:
            return f"Tool #{self._tool_order + 1}: No tool selected"

        # Get config mode
        radios = self.query_one("#config-mode-radios", RadioSet)
        pressed = radios.pressed_button
        config_mode = pressed.id if pressed else "mode-none"

        if config_mode == "mode-inline":
            config_input = self.query_one("#config-input", TextArea)
            config_text = config_input.text.strip()
            if config_text:
                try:
                    json.loads(config_text)
                except json.JSONDecodeError as e:
                    return (
                        f"Tool #{self._tool_order + 1}: Invalid JSON config - {e.msg}"
                    )
        elif config_mode == "mode-reference":
            config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
            if config_picker.selected_config is None:
                return f"Tool #{self._tool_order + 1}: No saved config selected"

        return None

    def get_alias(self) -> str:
        """Get the effective alias for this tool."""
        alias_input = self.query_one("#alias-input", Input)
        alias = alias_input.value.strip()
        if alias:
            return alias
        tool_picker = self.query_one("#tool-picker", ToolPicker)
        if tool_picker.selected_tool:
            return tool_picker.selected_tool.id
        return ""

    def set_tool_order(self, order: int) -> None:
        """Update the tool order and title."""
        self._tool_order = order
        title = self.query_one("#tool-title", Static)
        title.update(f"Tool #{order + 1}")

    def populate_from_response(self, tool: AgentToolResponse) -> None:
        """Populate the editor from an AgentToolResponse.

        Args:
            tool: Existing tool configuration to load
        """
        self._populate_from_response_async(tool)

    @work(exclusive=True)
    async def _populate_from_response_async(self, tool: AgentToolResponse) -> None:
        """Async population to fetch full tool details."""
        # Fetch full tool details to get parameter_schema
        try:
            tool_response = await self._client.get_tool(tool.tool_id)
            self._selected_tool = tool_response
        except Exception:
            # Create minimal tool response if fetch fails
            self._selected_tool = ToolResponse(
                id=tool.tool_id,
                description="",
                parameter_schema=None,
            )

        # Set tool picker
        tool_picker = self.query_one("#tool-picker", ToolPicker)
        tool_picker.set_initial_tool(self._selected_tool)

        # Set alias
        alias_input = self.query_one("#alias-input", Input)
        if tool.tool_alias and tool.tool_alias != tool.tool_id:
            alias_input.value = tool.tool_alias

        # Set config mode and values
        mode = resolve_agent_tool_mode(tool)
        if mode == "inline":
            # Inline config mode
            radio = self.query_one("#mode-inline", RadioButton)
            radio.value = True
            config_input = self.query_one("#config-input", TextArea)
            config_input.text = json.dumps(
                getattr(tool, "submitted_config", None), indent=2
            )
        elif mode == "reference":
            # Reference mode
            radio = self.query_one("#mode-reference", RadioButton)
            radio.value = True
            # Fetch and set tool config
            config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
            config_picker.tool_id = tool.tool_id
            tool_config_id = getattr(tool, "tool_config_id", None)
            if tool.tool_config_name and tool_config_id is not None:
                # Create a minimal summary for display
                summary = ToolConfigSummary(
                    id=tool_config_id,
                    name=tool.tool_config_name,
                    slug="",
                    tool_id=tool.tool_id,
                    description=None,
                    organization_id=None,
                    user_id=None,
                    created_at=datetime.now(timezone.utc),
                    updated_at=datetime.now(timezone.utc),
                )
                config_picker.set_initial_config(summary)
        else:
            # No config mode (default)
            radio = self.query_one("#mode-none", RadioButton)
            radio.value = True

        # Update visibility
        self._update_config_sections()
        self._update_schema_display()

        # Set enabled
        enabled_checkbox = self.query_one("#enabled-checkbox", Checkbox)
        enabled_checkbox.value = tool.is_enabled

        # Set LLM instruction
        if tool.llm_instruction:
            llm_input = self.query_one("#llm-instruction-input", TextArea)
            llm_input.text = tool.llm_instruction

        # Update order
        self._tool_order = tool.tool_order
        self.set_tool_order(tool.tool_order)
