# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""Dashboard adapter for the ConnectMixin wizard.

Bridges the async wizard protocol to Flask's synchronous request/response
model by queuing wizard output as JSON-serialisable dicts and draining them
after each user action.
"""

from __future__ import annotations

import asyncio
import logging
import threading
from collections import deque

from .connect_wizard import ConnectMixin
from .connect_wizard._helpers import TAB_CATEGORIES, TAB_ORDER
from .formatting import FormatMode

logger = logging.getLogger(__name__)


class DashboardWizardHost(ConnectMixin):
    """Wizard adapter for the localhost Flask dashboard.

    All ``wizard_send*`` methods push JSON dicts onto a per-session deque.
    The sync helper methods (``start_wizard``, ``select_menu``, etc.) run
    the corresponding async method in a throwaway event loop and return
    the accumulated messages via ``drain_messages()``.
    """

    format_mode = FormatMode.HTML
    supports_buttons = True
    supports_message_deletion = False

    def __init__(self, agent):
        self.agent = agent
        self._messages: deque[dict] = deque()
        self._lock = threading.Lock()
        self._wizard_state: dict[str, dict] = {}

    # ── Message queue ───────────────────────────────────────────────

    def _push(self, msg: dict) -> None:
        with self._lock:
            self._messages.append(msg)

    def drain_messages(self) -> list[dict]:
        with self._lock:
            msgs = list(self._messages)
            self._messages.clear()
        return msgs

    # ── WizardHost protocol implementation ──────────────────────────

    async def wizard_send(self, recipient_id: str, text: str) -> None:
        self._push({"type": "text", "text": text})

    async def wizard_send_menu(
        self,
        recipient_id: str,
        text: str,
        options: list[tuple[str, str]],
    ) -> None:
        self._push(
            {
                "type": "menu",
                "text": text,
                "options": [{"key": k, "label": lbl} for k, lbl in options],
            }
        )

    async def wizard_delete_message(self, recipient_id: str, message_ref: object) -> bool:
        return False

    async def wizard_send_card_menu(
        self,
        recipient_id: str,
        header: str,
        tab_content_text: str,
        tab_options: list[tuple[str, str]],
        active_tab: str,
    ) -> None:
        """Emit structured card data for the dashboard frontend."""
        tabs = []
        for tid in TAB_ORDER:
            cat = TAB_CATEGORIES[tid]
            tabs.append(
                {
                    "id": tid,
                    "label": cat["label"],
                    "icon": cat["icon"],
                    "active": tid == active_tab,
                }
            )

        self._push(
            {
                "type": "card_menu",
                "header": header,
                "tabs": tabs,
                "tab_content": tab_content_text,
                "tab_options": [{"key": k, "label": lbl} for k, lbl in tab_options],
                "active_tab": active_tab,
            }
        )

    def _get_current_provider_name(self) -> str:
        provider = getattr(self.agent, "provider", None)
        return provider.name if provider else "unknown"

    # ── Sync wrappers for Flask routes ──────────────────────────────

    def _run_async(self, coro):
        """Run an async coroutine in a throwaway event loop."""
        loop = asyncio.new_event_loop()
        try:
            loop.run_until_complete(coro)
        finally:
            loop.close()

    def start_wizard(self, args: list[str] | None = None) -> list[dict]:
        self._run_async(self.handle_connect_command("dashboard", args or []))
        return self.drain_messages()

    def select_menu(self, key: str) -> list[dict]:
        self._run_async(self.handle_connect_menu_selection("dashboard", key))
        return self.drain_messages()

    def send_text(self, text: str) -> list[dict]:
        self._run_async(self.handle_wizard_message("dashboard", text))
        return self.drain_messages()

    def switch_tab(self, tab_id: str) -> list[dict]:
        self._run_async(self._show_tab("dashboard", tab_id))
        return self.drain_messages()

    def is_active(self) -> bool:
        self._ensure_wizard_state()
        return bool(self._wizard_state.get("dashboard"))

    def start(self) -> list[dict]:
        self._run_async(self.handle_start_command("dashboard"))
        return self.drain_messages()

    def browse(self) -> list[dict]:
        self._run_async(self.handle_browse_command("dashboard"))
        return self.drain_messages()

    def search(self, service: str, query: str) -> list[dict]:
        self._run_async(self.handle_search_command("dashboard", [service] + query.split()))
        return self.drain_messages()
