"""Chat and agent interaction handler mixin for IeumApp."""
# ruff: noqa: BLE001, PLR0912, PLR2004, S110

from __future__ import annotations

import asyncio
import subprocess
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    pass


class ChatHandlerMixin:
    """Chat input and agent streaming methods for IeumApp."""

    async def _handle_bash_command(self, command: str) -> None:
        """Handle a bash command (! prefix).

        Args:
            command: The bash command to execute
        """
        from ieum.widgets.messages import (
            AssistantMessage,
            ErrorMessage,
            SystemMessage,
            UserMessage,
        )

        from textual.containers import VerticalScroll

        # Mount user message showing the bash command
        await self._mount_message(UserMessage(f"!{command}"))

        # Execute the bash command (shell=True is intentional for user-requested bash)
        timeout = self.BASH_COMMAND_TIMEOUT
        try:
            result = await asyncio.to_thread(  # noqa: S604
                subprocess.run,
                command,
                shell=True,
                capture_output=True,
                text=True,
                cwd=self._cwd,
                timeout=timeout,
            )
            output = result.stdout.strip()
            if result.stderr:
                output += f"\n[stderr]\n{result.stderr.strip()}"

            if output:
                # Display output as assistant message (uses markdown for code blocks)
                msg = AssistantMessage(f"```\n{output}\n```")
                await self._mount_message(msg)
                await msg.write_initial_content()
            else:
                await self._mount_message(SystemMessage("Command completed (no output)"))

            if result.returncode != 0:
                await self._mount_message(ErrorMessage(f"Exit code: {result.returncode}"))

            # Scroll to show the output (user-initiated command, so scroll is expected)
            chat = self.query_one("#chat", VerticalScroll)
            chat.scroll_end(animate=False)

        except subprocess.TimeoutExpired:
            await self._mount_message(
                ErrorMessage(
                    f"Command timed out ({timeout}s limit). "
                    "Try running long commands in background."
                )
            )
        except OSError as e:
            await self._mount_message(ErrorMessage(str(e)))

    async def _handle_user_message(self, message: str) -> None:
        """Handle a user message to send to the agent.

        Args:
            message: The user's message
        """
        from ieum.widgets.messages import SystemMessage, UserMessage

        from textual.containers import VerticalScroll

        # Inject plan mode preamble if active
        if (
            self._session_state
            and self._session_state.plan_mode
            and "## PLAN MODE ACTIVE" not in message
        ):
            from ieum.plan_mode import PLAN_MODE_PREAMBLE

            message = f"{PLAN_MODE_PREAMBLE}\n\n## Task\n{message}"

        # Mount the user message
        await self._mount_message(UserMessage(message))

        # Save user message to conversation history
        if self._session_state:
            from ieum.sessions import save_conversation_message
            asyncio.create_task(
                save_conversation_message(self._session_state.thread_id, "user", message)
            )

        # Scroll to bottom when user sends a new message
        chat = self.query_one("#chat", VerticalScroll)
        if chat.max_scroll_y > 0:
            chat.scroll_end(animate=False)

        # Check if agent is available
        if self._agent and self._ui_adapter and self._session_state:
            self._agent_running = True

            # Disable submission while agent is working (user can still type)
            if self._chat_input:
                self._chat_input.set_cursor_active(active=False)
                self._chat_input.set_submit_enabled(enabled=False)

            # Use run_worker to avoid blocking the main event loop
            # This allows the UI to remain responsive during agent execution
            self._agent_worker = self.run_worker(
                self._run_agent_task(message),
                exclusive=False,
            )
        else:
            if self._setup_mode:
                await self._mount_message(
                    SystemMessage(
                        "⚠️ API 키가 설정되지 않아 AI와 대화할 수 없습니다.\n"
                        "/env set <KEY> <value> 로 API 키를 설정한 후 재시작하세요."
                    )
                )
            else:
                await self._mount_message(
                    SystemMessage("Agent not configured. Run with --agent flag or use standalone mode.")
                )

    async def _run_agent_task(self, message: str) -> None:
        """Run the agent task in a background worker.

        This runs in a worker thread so the main event loop stays responsive.
        """
        from ieum.textual_adapter import execute_task_textual
        from ieum.widgets.messages import ErrorMessage, SystemMessage

        try:
            await execute_task_textual(
                user_input=message,
                agent=self._agent,
                assistant_id=self._assistant_id,
                session_state=self._session_state,
                adapter=self._ui_adapter,
                backend=self._backend,
            )
            # Capture plan from completed response if in plan mode
            if self._session_state and self._session_state.plan_mode and self._ui_adapter:
                plan_text = getattr(self._ui_adapter, "_last_completed_content", "")
                if plan_text:
                    self._session_state.current_plan = plan_text
                    await self._mount_message(
                        SystemMessage(
                            "Plan captured. Use '/plan show' to review, '/plan approve' to execute."
                        )
                    )

            # Auto-verify loop: run tests/lint after agent edits
            if (
                self._session_state
                and getattr(self._session_state, "verify_enabled", False)
                and self._session_state.verify_config
                and not self._session_state.plan_mode
            ):
                await self._run_verify_loop(message)

            # Auto-memory suggestion: remind user to save learnings periodically
            if self._session_state and not self._session_state.plan_mode:
                self._session_state._user_message_count += 1
                from ieum.auto_memory import AUTO_REMEMBER_INTERVAL, AUTO_REMEMBER_MIN_MESSAGES
                count = self._session_state._user_message_count
                if (
                    count >= AUTO_REMEMBER_MIN_MESSAGES
                    and count % AUTO_REMEMBER_INTERVAL == 0
                ):
                    await self._mount_message(
                        SystemMessage(
                            "Tip: Use /remember to save learnings from this session, "
                            "or /memory to manage stored memories."
                        )
                    )
        except Exception as e:
            await self._mount_message(ErrorMessage(f"Agent error: {e}"))
        finally:
            # Clean up loading widget and agent state
            await self._cleanup_agent_task()

    async def _run_verify_loop(self, original_message: str) -> None:
        """Run post-execution verification loop.

        Runs test/lint commands and feeds failures back to the agent
        for automatic fixing, up to max_retries.

        Args:
            original_message: The original user message (for context).
        """
        from ieum.textual_adapter import execute_task_textual
        from ieum.verify_loop import build_feedback_message, run_verify
        from ieum.widgets.messages import SystemMessage

        cfg = self._session_state.verify_config
        if not cfg or (not cfg.test_command and not cfg.lint_command):
            return

        for attempt in range(1, cfg.max_retries + 1):
            await self._mount_message(
                SystemMessage(f"Verify ({attempt}/{cfg.max_retries})...")
            )
            result = await run_verify(cfg, cwd=self._cwd)
            await self._mount_message(SystemMessage(result.summary))

            if result.all_passed:
                return

            # Feed failures back to agent for fixing
            feedback = build_feedback_message(result)
            fix_prompt = (
                f"{feedback}\n\n"
                f"Fix the errors above. Original task: {original_message[:500]}"
            )
            try:
                await execute_task_textual(
                    user_input=fix_prompt,
                    agent=self._agent,
                    assistant_id=self._assistant_id,
                    session_state=self._session_state,
                    adapter=self._ui_adapter,
                    backend=self._backend,
                )
            except Exception as e:
                await self._mount_message(
                    SystemMessage(f"Verify fix attempt failed: {e}")
                )
                return

        # Final verification
        final = await run_verify(cfg, cwd=self._cwd)
        if final.all_passed:
            await self._mount_message(SystemMessage("All checks passed after fixes."))
        else:
            await self._mount_message(
                SystemMessage(f"Verify still failing after {cfg.max_retries} attempts: {final.summary}")
            )

    async def _cleanup_agent_task(self) -> None:
        """Clean up after agent task completes or is cancelled."""
        self._agent_running = False
        self._agent_worker = None

        # Remove thinking spinner if present
        await self._hide_thinking()

        # Re-enable submission now that agent is done
        if self._chat_input:
            self._chat_input.set_cursor_active(active=True)
            self._chat_input.set_submit_enabled(enabled=True)

        # Ensure token display is restored (in case of early cancellation)
        if self._token_tracker:
            self._token_tracker.show()
