"""Agents screen for browsing and managing AI agents.

This screen provides:
- Search and browse agents
- View agent details in preview panel
- Create new agents
- Copy agent names
"""

from typing import TYPE_CHECKING

from textual import on, work
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.widgets import Button, DataTable, Input, Static

from alloy_runtime_types.dtos.agents import AgentSummary

from cli.infrastructure.scope_utils import format_scope_label
from cli.infrastructure.tui.clipboard import copy_to_clipboard
from cli.infrastructure.tui.formatters import format_datetime
from cli.tui.screens.base import BrowserWidget
from cli.tui.screens.nav_screen import NavScreen
from cli.tui.widgets.agent_create_modal import AgentCreateModal
from cli.tui.widgets.agent_form_modal import AgentFormResult
from cli.tui.widgets.agent_update_modal import AgentUpdateModal
from cli.tui.widgets.confirm_modal import ConfirmModal

if TYPE_CHECKING:
    from cli.tui.app import AlloyRuntimeApp


class AgentsWidget(BrowserWidget[AgentSummary]):
    """Widget for browsing and managing agents."""

    app: "AlloyRuntimeApp"

    TITLE = "Agents"
    TABLE_COLUMNS = ["Name", "Scope", "Models", "Updated"]

    def compose(self) -> ComposeResult:
        """Compose the browser layout with a New Agent button."""
        with Vertical(id="browser-root"):
            with Vertical(id="search-container"):
                with Horizontal(id="search-row"):
                    yield Input(
                        placeholder=f"Search {self.TITLE.lower()}...",
                        id="search-input",
                    )
                    yield Button("+ New", id="new-agent-btn", variant="primary")
                yield Static("Loading...", id="status-bar")

            with Horizontal(id="main-container"):
                with Vertical(id="list-container"):
                    yield DataTable(id="data-table", cursor_type="row")

                with Vertical(id="detail-container"):
                    yield Static("Preview", id="preview-title")
                    with VerticalScroll(id="preview-scroll"):
                        yield Static(
                            "Select an item to view details",
                            id="preview-content",
                        )

    @on(Button.Pressed, "#new-agent-btn")
    def _on_new_agent_btn_pressed(self, event: Button.Pressed) -> None:
        """Handle New Agent button press."""
        self.open_new_agent_modal()

    async def _fetch_items(self, query: str) -> tuple[list[AgentSummary], int]:
        """Fetch agents from the API."""
        client = await self._ensure_client()
        response = await client.list_agents(
            search=query if query else None,
            limit=50,
        )

        # Update agent cache for cross-screen use (e.g., chat agent picker)
        self.app.store.agent_cache = list(response.agents)

        return response.agents, response.total

    def _format_row(self, item: AgentSummary) -> tuple[str, ...]:
        """Format an agent as a table row."""
        return (
            item.name,
            format_scope_label(
                str(item.organization_id) if item.organization_id else None,
                str(item.user_id) if item.user_id else None,
            ),
            f"{item.model_count} models",
            format_datetime(item.updated_at),
        )

    def _update_preview(self, item: AgentSummary) -> None:
        """Update the preview pane with agent details."""
        preview = self.query_one("#preview-content", Static)

        scope = format_scope_label(
            str(item.organization_id) if item.organization_id else None,
            str(item.user_id) if item.user_id else None,
        )

        created = format_datetime(item.created_at)
        updated = format_datetime(item.updated_at)

        system_instruction_id = (
            str(item.system_instruction_version_id)
            if item.system_instruction_version_id
            else "-"
        )
        input_schema_id = str(item.input_schema_id) if item.input_schema_id else "-"
        output_schema_id = str(item.output_schema_id) if item.output_schema_id else "-"

        preview_text = f"""[bold]Agent ID:[/] {item.id}
[bold]Name:[/] {item.name}
[bold]Scope:[/] {scope}
[bold]Models:[/] {item.model_count}

[bold]Description:[/]
{item.description or "(no description)"}

[bold]System Instruction Version ID:[/]
{system_instruction_id}

[bold]Input Schema ID:[/]
{input_schema_id}

[bold]Output Schema ID:[/]
{output_schema_id}

[bold]Created:[/] {created}
[bold]Updated:[/] {updated}"""

        preview.update(preview_text)

    # =========================================================================
    # Public methods for parent Screen to call
    # =========================================================================

    def copy_agent_name(self) -> None:
        """Copy the agent name to clipboard."""
        agent = self.get_selected_item()
        if not agent:
            self.app.notify("No agent selected", severity="warning")
            return

        copy_to_clipboard(self.app, agent.name, "Agent name")

    def open_new_agent_modal(self) -> None:
        """Open the agent creation modal."""
        self._open_agent_create_modal()

    def open_edit_agent_modal(self) -> None:
        """Open the agent update modal for the selected agent."""
        agent = self.get_selected_item()
        if not agent:
            self.app.notify("No agent selected", severity="warning")
            return
        self._open_agent_update_modal(agent)

    def delete_selected_agent(self) -> None:
        """Delete the currently selected agent."""
        agent = self.get_selected_item()
        if not agent:
            self.app.notify("No agent selected", severity="warning")
            return

        agent_name = agent.name
        self.app.push_screen(
            ConfirmModal(
                title="Delete Agent",
                message=f"Delete agent '{agent_name}'? This cannot be undone.",
            ),
            lambda confirmed: self._on_delete_confirmed(confirmed, agent_name),
        )

    # =========================================================================
    # Internal async operations
    # =========================================================================

    @work(exclusive=True)
    async def _open_agent_create_modal(self) -> None:
        """Open the agent creation modal."""
        try:
            client = await self._ensure_client()
            self.app.push_screen(
                AgentCreateModal(client=client),
                self._on_agent_created,
            )
        except Exception as e:
            self.app.notify(f"Failed to open agent creator: {e}", severity="error")

    def _on_agent_created(self, result: AgentFormResult | None) -> None:
        """Callback when agent creation modal is dismissed."""
        if result is not None:
            self.app.notify(
                f"Created agent: {result.agent_name}",
                severity="information",
            )
            # Refresh the agent list
            self._do_search(self._last_search)

    @work(exclusive=True)
    async def _open_agent_update_modal(self, agent_summary: AgentSummary) -> None:
        """Fetch full agent details and open the update modal."""
        try:
            client = await self._ensure_client()
            # Fetch full agent details (summary doesn't have all fields)
            agent = await client.get_agent(str(agent_summary.id))
            self.app.push_screen(
                AgentUpdateModal(client=client, agent=agent),
                self._on_agent_updated,
            )
        except Exception as e:
            self.app.notify(f"Failed to open agent editor: {e}", severity="error")

    def _on_agent_updated(self, result: AgentFormResult | None) -> None:
        """Callback when agent update modal is dismissed."""
        if result is not None:
            self.app.notify(
                f"Updated agent: {result.agent_name}",
                severity="information",
            )
            # Refresh the agent list
            self._do_search(self._last_search)

    def _on_delete_confirmed(self, confirmed: bool | None, agent_name: str) -> None:
        """Callback when delete confirmation modal is dismissed."""
        if confirmed:
            self._execute_delete(agent_name)

    @work(exclusive=True)
    async def _execute_delete(self, agent_name: str) -> None:
        """Execute the delete operation."""
        try:
            client = await self._ensure_client()
            await client.delete_agent(agent_name)

            self.app.notify(
                f"Agent '{agent_name}' deleted successfully",
                severity="information",
            )
            # Refresh the agent list
            self._do_search(self._last_search)

        except Exception as e:
            error_msg = str(e)
            if "pipeline" in error_msg.lower():
                self.app.notify(
                    "Cannot delete: agent is referenced by pipelines",
                    severity="error",
                )
            else:
                self.app.notify(f"Failed to delete agent: {e}", severity="error")


class AgentsScreen(NavScreen):
    """Screen for browsing and managing agents.

    This is a proper Textual Screen that wraps the AgentsWidget
    and provides keybindings that work regardless of focus.
    """

    app: "AlloyRuntimeApp"

    SCREEN_ID = "agents"

    DEFAULT_CSS = """
    AgentsScreen #screen-root {
        height: 1fr;
    }
    
    AgentsScreen .browser-widget {
        height: 100%;
        width: 100%;
    }
    """

    # Screen-specific bindings (Ctrl+ prefixed)
    # NavScreen.BINDINGS provides global navigation (c, a, t, s, m, d, ?)
    BINDINGS = NavScreen.BINDINGS + [
        Binding("ctrl+f", "focus_search", "Search", show=True),
        Binding("ctrl+r", "refresh", "Refresh", show=True),
        Binding("ctrl+y", "copy_name", "Copy Name", show=True),
        Binding("ctrl+n", "new_agent", "New", show=True),
        Binding("ctrl+e", "edit_agent", "Edit", show=True),
        Binding("ctrl+d", "delete_agent", "Delete", show=True),
        Binding("escape", "focus_table", "Focus List", show=False),
        Binding("enter", "copy_name", "Copy Name", show=False),
    ]

    def compose_content(self) -> ComposeResult:
        """Compose the screen content."""
        yield AgentsWidget(id="agents-widget")

    def on_screen_resume(self) -> None:
        """Focus the data table when screen becomes active."""
        self.call_after_refresh(self._focus_table)

    def _focus_table(self) -> None:
        """Focus the data table."""
        try:
            widget = self.query_one("#agents-widget", AgentsWidget)
            widget.focus_table()
        except Exception:
            pass

    def _get_widget(self) -> AgentsWidget:
        """Get the agents widget."""
        return self.query_one("#agents-widget", AgentsWidget)

    # =========================================================================
    # Actions (keybinding handlers)
    # =========================================================================

    def action_focus_search(self) -> None:
        """Focus the search input."""
        self._get_widget().focus_search()

    def action_focus_table(self) -> None:
        """Focus the data table."""
        self._get_widget().focus_table()

    def action_refresh(self) -> None:
        """Refresh the current search results."""
        self._get_widget().refresh_data()

    def action_copy_name(self) -> None:
        """Copy the agent name to clipboard."""
        self._get_widget().copy_agent_name()

    def action_new_agent(self) -> None:
        """Open the agent creation modal."""
        self._get_widget().open_new_agent_modal()

    def action_edit_agent(self) -> None:
        """Open the agent update modal."""
        self._get_widget().open_edit_agent_modal()

    def action_delete_agent(self) -> None:
        """Delete the selected agent."""
        self._get_widget().delete_selected_agent()
