"""
FastStream Event Bus — Centralized pub/sub cho Canifa Chatbot.

Dùng Redis Streams làm message broker. Tất cả events trong hệ thống
đều publish/subscribe qua module này.

Usage:
    from common.event_bus import event_bus

    # Publish event (fire-and-forget, gọi từ bất kỳ đâu)
    await event_bus.publish("chat:search_completed", {
        "user_id": "abc",
        "query": "áo phông nam",
        "products_found": 12,
    })
"""

import json
import logging
from datetime import datetime
from typing import Any

from pydantic import BaseModel

from config import REDIS_CACHE_PORT, REDIS_CACHE_URL, REDIS_PASSWORD, REDIS_USERNAME

logger = logging.getLogger(__name__)


# ═══════════════════════════════════════════════
# Event Models (Pydantic — FastStream auto-validates)
# ═══════════════════════════════════════════════

class ChatSearchEvent(BaseModel):
    """Emitted khi AI Agent hoàn tất tìm kiếm sản phẩm."""
    user_id: str
    query: str
    products_found: int = 0
    tier: int = 0
    elapsed_ms: float = 0
    keywords: list[str] = []
    tags: list[str] = []
    timestamp: str = ""


class FeedbackEvent(BaseModel):
    """Emitted khi user gửi feedback (like/dislike)."""
    user_id: str
    trace_id: str = ""
    feedback_type: str = ""  # "like" | "dislike" | "complaint"
    message: str = ""
    timestamp: str = ""


class ProductSyncEvent(BaseModel):
    """Emitted khi cần sync product data (StarRocks → Meilisearch)."""
    action: str = "full_sync"  # "full_sync" | "incremental"
    triggered_by: str = "manual"
    timestamp: str = ""


class ChatHistoryEvent(BaseModel):
    """Emitted để tiến hành lưu lịch sử chat vào DB, fix lỗi Langfuse trace / reload."""
    identity_key: str
    human_query: str
    ai_response: dict | None = None
    conversation_id: str | None = None
    timestamp: str = ""


# ═══════════════════════════════════════════════
# Event Bus Singleton
# ═══════════════════════════════════════════════

class EventBus:
    """
    Lightweight wrapper quanh FastStream RedisBroker.
    Chạy TRONG cùng process FastAPI (không cần worker riêng).
    """

    def __init__(self):
        self._broker = None
        self._app = None
        self._started = False

    def _build_redis_url(self) -> str:
        """Build redis:// URL từ config env vars."""
        host = REDIS_CACHE_URL or "localhost"
        port = REDIS_CACHE_PORT or 6379

        if REDIS_USERNAME and REDIS_PASSWORD:
            return f"redis://{REDIS_USERNAME}:{REDIS_PASSWORD}@{host}:{port}/3"
        elif REDIS_PASSWORD:
            return f"redis://:{REDIS_PASSWORD}@{host}:{port}/3"
        else:
            return f"redis://{host}:{port}/3"

    async def start(self):
        """Initialize and start FastStream broker. Call on FastAPI startup."""
        if self._started:
            return

        try:
            from faststream.redis import RedisBroker

            redis_url = self._build_redis_url()
            self._broker = RedisBroker(redis_url)

            # Register subscribers TRƯỚC khi start
            self._register_subscribers()

            await self._broker.start()
            self._started = True
            logger.info("EventBus started: FastStream + Redis (db=3)")

        except Exception as e:
            logger.warning("EventBus failed to start (non-fatal): %s", e)
            self._started = False

    async def stop(self):
        """Graceful shutdown. Call on FastAPI shutdown."""
        if self._broker and self._started:
            try:
                await self._broker.close()
                logger.info("EventBus stopped")
            except Exception as e:
                logger.warning("EventBus stop error: %s", e)
            finally:
                self._started = False

    async def publish(self, channel: str, data: dict[str, Any]):
        """
        Publish event lên Redis Stream.
        Fire-and-forget: nếu broker chưa start thì skip silently.
        """
        if not self._started or not self._broker:
            return

        try:
            # Inject timestamp nếu chưa có
            if "timestamp" not in data or not data["timestamp"]:
                data["timestamp"] = datetime.now().isoformat()

            await self._broker.publish(
                message=json.dumps(data, ensure_ascii=False, default=str),
                channel=channel,
            )
            logger.debug("Published event to '%s'", channel)

        except Exception as e:
            # KHÔNG raise — event bus lỗi không được crash main flow
            logger.warning("EventBus publish error on '%s': %s", channel, e)

    def _register_subscribers(self):
        """Register tất cả consumers tại đây."""
        if not self._broker:
            return

        @self._broker.subscriber("chat:search_completed")
        async def on_search_completed(msg: str):
            """Consumer: Log search analytics."""
            try:
                data = json.loads(msg)
                logger.info(
                    "[EVENT] search_completed | user=%s query='%s' found=%s tier=%s time=%.0fms",
                    data.get("user_id", "?"),
                    data.get("query", "?")[:40],
                    data.get("products_found", 0),
                    data.get("tier", "?"),
                    data.get("elapsed_ms", 0),
                )
            except Exception as e:
                logger.warning("[EVENT] Failed to process search event: %s", e)

        @self._broker.subscriber("chat:feedback")
        async def on_feedback(msg: str):
            """Consumer: Log feedback events."""
            try:
                data = json.loads(msg)
                logger.info(
                    "[EVENT] feedback | user=%s type=%s trace=%s",
                    data.get("user_id", "?"),
                    data.get("feedback_type", "?"),
                    data.get("trace_id", "?"),
                )
            except Exception as e:
                logger.warning("[EVENT] Failed to process feedback event: %s", e)

        @self._broker.subscriber("product:sync_requested")
        async def on_sync_requested(msg: str):
            """Consumer: Trigger product sync pipeline."""
            try:
                data = json.loads(msg)
                logger.info(
                    "[EVENT] sync_requested | action=%s by=%s",
                    data.get("action", "full_sync"),
                    data.get("triggered_by", "?"),
                )
                # TODO: Hook vào Meilisearch sync script ở đây
            except Exception as e:
                logger.warning("[EVENT] Failed to process sync event: %s", e)


# ─── Singleton ───
event_bus = EventBus()
