from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from card_store import (
    delete_cards_for_chat,
    ensure_card_store_dirs,
    sync_templates_context_file,
)
from inbox_service import InboxService
from nanobot_api_client import send_nanobot_api_request
from rtc_manager import RtcSessionManager
from session_store import ensure_session_store_dirs
from supertonic_gateway import SuperTonicGateway
from tool_job_service import ToolJobService
from ui_event_bus import UiEventBus
from workbench_store import ensure_workbench_store_dirs


def normalize_event_chat_id(normalize_chat_id, raw_chat_id: Any) -> str | None:
    chat_id = normalize_chat_id(str(raw_chat_id or ""))
    return chat_id or None


@dataclass(slots=True)
class WebAppRuntime:
    gateway: SuperTonicGateway
    event_bus: UiEventBus
    inbox_service: InboxService
    tool_job_service: ToolJobService
    rtc_manager: RtcSessionManager | None
    list_sessions: Callable[[], list[dict[str, Any]]]
    normalize_chat_id: Callable[[str], str]

    def open_inbox_items(self, *, limit: int = 4) -> list[dict[str, Any]]:
        return self.inbox_service.open_items(limit=limit)

    async def publish_sessions_changed(self) -> None:
        await self.event_bus.publish(
            {
                "type": "sessions.changed",
                "sessions": self.list_sessions(),
            },
            broadcast=True,
        )

    async def publish_cards_changed(self, chat_id: Any = None) -> None:
        target_chat_id = normalize_event_chat_id(self.normalize_chat_id, chat_id)
        await self.event_bus.publish(
            {"type": "cards.changed", "chat_id": target_chat_id},
            chat_id=target_chat_id,
            broadcast=target_chat_id is None,
        )

    async def publish_workbench_changed(self, chat_id: Any = None) -> None:
        target_chat_id = normalize_event_chat_id(self.normalize_chat_id, chat_id)
        await self.event_bus.publish(
            {"type": "workbench.changed", "chat_id": target_chat_id},
            chat_id=target_chat_id,
            broadcast=target_chat_id is None,
        )

    async def publish_inbox_changed(self) -> None:
        await self.event_bus.publish(
            {
                "type": "inbox.changed",
                "items": self.open_inbox_items(),
            },
            broadcast=True,
        )

    async def shutdown(self) -> None:
        await self.tool_job_service.shutdown()
        if self.rtc_manager is not None:
            await self.rtc_manager.shutdown()
        await self.gateway.shutdown()

    @staticmethod
    def delete_cards_for_chat(chat_id: str) -> int:
        return delete_cards_for_chat(chat_id)


def create_web_runtime(
    *,
    repo_dir: Path,
    inbox_dir: Path,
    tool_job_timeout_seconds: float,
    tool_job_retention_seconds: float,
    list_sessions,
    normalize_chat_id,
) -> WebAppRuntime:
    ensure_card_store_dirs()
    ensure_session_store_dirs()
    ensure_workbench_store_dirs()
    inbox_dir.mkdir(parents=True, exist_ok=True)
    sync_templates_context_file()

    gateway = SuperTonicGateway()
    event_bus = UiEventBus()
    inbox_service = InboxService(repo_dir=repo_dir, inbox_dir=inbox_dir)
    tool_job_service = ToolJobService(
        send_request=lambda method, params: send_nanobot_api_request(
            method,
            params,
            timeout_seconds=tool_job_timeout_seconds,
        ),
        timeout_seconds=tool_job_timeout_seconds,
        retention_seconds=tool_job_retention_seconds,
    )
    runtime = WebAppRuntime(
        gateway=gateway,
        event_bus=event_bus,
        inbox_service=inbox_service,
        tool_job_service=tool_job_service,
        rtc_manager=None,
        list_sessions=list_sessions,
        normalize_chat_id=normalize_chat_id,
    )
    runtime.rtc_manager = RtcSessionManager(
        gateway=gateway,
        publish_cards_changed=runtime.publish_cards_changed,
        publish_workbench_changed=runtime.publish_workbench_changed,
        publish_sessions_changed=runtime.publish_sessions_changed,
    )
    return runtime
