"""Root Textual application — 60/40 split-pane installer + agent chat.

Phase 5+: Agent Johnny 5 with MCP, UI polish, and agent recovery tools.

Concurrency boundaries:
  - Installer (StepRunner): @work(exclusive=True) async on the main loop.
    Uses asyncio.create_subprocess_shell — truly non-blocking.
  - Agent (AgentBrain): @work(thread=True) in a BACKGROUND THREAD.
    Uses httpx.Client (sync) for streaming, subprocess.run for tools.
    MCP connection established lazily in the agent thread.
    All UI updates routed via app.call_from_thread().
"""

from __future__ import annotations

import asyncio
import os

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual import work

from .messages import (
    AgentReply,
    AgentToolCall,
    AgentTyping,
    LogLine,
    StepStarted,
    StepCompleted,
    InstallFinished,
    UserChatSubmit,
)
from .state import InstallState
from .constants import BUILTIN_API_KEY, OPENROUTER_MODEL
from .installer.runner import StepRunner
from .agent.brain import AgentBrain
from .ui.log_pane import LogPane
from .ui.chat_pane import ChatPane
from .ui.status_bar import StatusBar


class EasyClawApp(App):
    """EasyClaw Agentic Installer — Agent Johnny 5 + MCP + recovery."""

    # Disable command palette (not useful for this app)
    ENABLE_COMMAND_PALETTE = False

    CSS = """
    Screen {
        layout: horizontal;
    }

    #log-pane {
        width: 3fr;
        height: 1fr;
        border-right: thick #444444;
    }

    #chat-pane {
        width: 2fr;
        height: 1fr;
    }

    /* Tame the focus ring — Textual default is bright red, unreadable */
    *:focus {
        border: tall #666666;
    }
    #chat-input:focus {
        border: tall #888888;
    }
    """

    TITLE = "EasyClaw Agentic Installer"
    SUB_TITLE = "Agent Johnny 5"

    BINDINGS = [
        Binding("q", "quit", "Quit", priority=True),
        Binding("ctrl+c", "quit", "Force Quit", priority=True),
    ]

    def __init__(
        self, api_key: str = "", mcp_url: str = "",
        mcp_key: str = "", **kwargs
    ) -> None:
        super().__init__(**kwargs)
        self.state = InstallState()
        self.step_runner = StepRunner(self.state, self)

        # Resolve API key: explicit arg > env var > built-in key (Kimi K2.5)
        resolved_key = (
            api_key
            or os.environ.get("OPENROUTER_API_KEY", "")
            or BUILTIN_API_KEY
        )
        # Resolve MCP key: explicit arg > env var > empty
        resolved_mcp_key = (
            mcp_key
            or os.environ.get("EASYCLAW_MCP_KEY", "")
        )

        self._api_key = resolved_key
        self._model = OPENROUTER_MODEL
        self._mcp_url = mcp_url
        self._mcp_key = resolved_mcp_key

        self.agent = AgentBrain(
            self.state, resolved_key, mcp_url=mcp_url,
            mcp_key=resolved_mcp_key, model=OPENROUTER_MODEL,
        )

    # ------------------------------------------------------------------
    # Compose
    # ------------------------------------------------------------------

    def compose(self) -> ComposeResult:
        yield LogPane(id="log-pane")
        yield ChatPane(id="chat-pane")
        yield StatusBar(id="status-bar")

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    async def on_mount(self) -> None:
        """Start the installer, greet the user, and init MCP."""
        # Disable mouse tracking — web terminals (Hostinger, etc.) don't
        # handle Textual's SGR mouse protocol and render escape codes as
        # garbage text like ^[[<35;150;37M across the screen.
        try:
            driver = self._driver
            if driver and hasattr(driver, "_disable_mouse_support"):
                driver._disable_mouse_support()
        except Exception:
            pass

        # Store main event loop for agent thread -> main loop scheduling
        self.state.main_loop = asyncio.get_event_loop()

        self.run_installer()

        # Show agent greeting in chat
        chat_pane = self.query_one("#chat-pane", ChatPane)
        chat_pane.add_agent_message(
            "I'm Johnny 5, your AI assistant. "
            "I can see the install logs and help troubleshoot. "
            "Ask me anything!"
        )
        # Kick off MCP connection in background thread
        if self._mcp_url:
            self.init_agent_mcp()

    # ------------------------------------------------------------------
    # Installer worker (async on main loop)
    # ------------------------------------------------------------------

    @work(exclusive=True)
    async def run_installer(self) -> None:
        """Drive the StepRunner on the main async loop."""
        await self.step_runner.run_all_steps()

    @work(exclusive=False)
    async def run_resume_pipeline(self, from_step: int) -> None:
        """Resume the installation pipeline from a specific step."""
        await self.step_runner.resume_from(from_step)

    # ------------------------------------------------------------------
    # Agent worker (BACKGROUND THREAD — critical boundary)
    # ------------------------------------------------------------------

    @work(thread=True)
    def init_agent_mcp(self) -> None:
        """Connect to MCP knowledge base in a background thread.

        Non-blocking — the UI starts immediately. MCP tools become
        available once the handshake completes (1-2 seconds).
        """
        self.agent.ensure_mcp(self)

    @work(thread=True)
    def send_to_agent(self, user_text: str) -> None:
        """Run the agent loop in a background thread.

        Uses httpx.Client (sync) for streaming, subprocess.run for
        tools. All UI updates via call_from_thread().
        """
        self.agent.handle_user_message(user_text, self)

    @work(thread=True)
    def agent_proactive_intervention(
        self, step_name: str, error: str
    ) -> None:
        """Agent auto-diagnoses a failed step (background thread)."""
        self.agent.handle_step_failure(step_name, error, self)

    # ------------------------------------------------------------------
    # Message handlers
    # ------------------------------------------------------------------

    def on_log_line(self, message: LogLine) -> None:
        """Route a log line to the left pane."""
        log_pane = self.query_one("#log-pane", LogPane)
        log_pane.append_line(message.text, is_stderr=message.is_stderr)

    def on_step_started(self, message: StepStarted) -> None:
        """Update the status bar when a step begins."""
        status = self.query_one("#status-bar", StatusBar)
        status.update_step(message.index, message.name, message.total)

    def on_step_completed(self, message: StepCompleted) -> None:
        """Record step result, show in log, trigger agent on failure."""
        # Track result in state
        self.state.step_results.append((message.name, message.success))

        # Update status bar counts
        status = self.query_one("#status-bar", StatusBar)
        status.record_step_result(message.success)

        # Color-coded result in log pane
        log_pane = self.query_one("#log-pane", LogPane)
        log_pane.append_step_result(
            message.index,
            message.name,
            self.state.total_steps,
            message.success,
            message.error,
        )

        # On failure, trigger agent proactive intervention
        if not message.success:
            self.agent_proactive_intervention(message.name, message.error)

    def on_install_finished(self, message: InstallFinished) -> None:
        """Update the status bar with final result."""
        status = self.query_one("#status-bar", StatusBar)
        status.show_final(message.success)

    def on_user_chat_submit(self, message: UserChatSubmit) -> None:
        """Handle chat input — show user message, dispatch to agent thread."""
        chat_pane = self.query_one("#chat-pane", ChatPane)
        chat_pane.add_user_message(message.text)
        self.send_to_agent(message.text)

    def on_agent_typing(self, message: AgentTyping) -> None:
        """Show typing indicator in chat."""
        chat_pane = self.query_one("#chat-pane", ChatPane)
        chat_pane.show_typing()

    def on_agent_reply(self, message: AgentReply) -> None:
        """Route agent's final reply to the chat pane."""
        chat_pane = self.query_one("#chat-pane", ChatPane)
        chat_pane.add_agent_message(message.text)

    def on_agent_tool_call(self, message: AgentToolCall) -> None:
        """Show tool-call indicator in chat."""
        chat_pane = self.query_one("#chat-pane", ChatPane)
        chat_pane.add_tool_indicator(message.tool_name, message.args)
