"""
WebSocket consumer for TheStarFX real-time market data stream.

MarketConsumer:
  - Accepts any browser connection at ws/market/
  - Joins the shared 'market_stream' channel group
  - Lazily starts the market simulator task on first connection
  - Relays all group messages to the connected client
  - Handles subscribe/unsubscribe action messages from the client
"""

import asyncio
import json
import logging

from channels.generic.websocket import AsyncWebsocketConsumer
from .market_simulator import market_simulator_loop

logger = logging.getLogger(__name__)


class MarketConsumer(AsyncWebsocketConsumer):
    """
    Async WebSocket consumer that relays real-time market data
    to connected browser clients.
    """

    # Class-level singleton simulator task — shared across all instances
    _simulator_task: asyncio.Task | None = None

    async def connect(self):
        """Accept connection and join the market_stream group."""
        await self.channel_layer.group_add("market_stream", self.channel_name)
        await self.accept()

        # Start the simulator if not already running
        if (
            MarketConsumer._simulator_task is None
            or MarketConsumer._simulator_task.done()
        ):
            logger.info("[Consumer] Starting market simulator task.")
            MarketConsumer._simulator_task = asyncio.create_task(
                market_simulator_loop()
            )

        # Send immediate connection acknowledgement
        await self.send(
            text_data=json.dumps(
                {
                    "type": "connection",
                    "status": "connected",
                    "message": "Connected to TheStarFX Market Stream",
                }
            )
        )
        logger.info(f"[Consumer] Client connected: {self.channel_name}")

    async def disconnect(self, close_code):
        """Leave the market_stream group on disconnect."""
        await self.channel_layer.group_discard("market_stream", self.channel_name)
        logger.info(f"[Consumer] Client disconnected ({close_code}): {self.channel_name}")

    async def receive(self, text_data):
        """
        Handle messages sent from the browser client.
        Supports 'subscribe' and 'unsubscribe' action messages
        for future per-client channel filtering.

        Expected format:
          { "action": "subscribe",   "channels": ["forex:majors", "crypto:leaders"] }
          { "action": "unsubscribe", "channels": ["forex:minors"] }
        """
        try:
            data = json.loads(text_data)
        except json.JSONDecodeError:
            return

        action = data.get("action")
        if action == "ping":
            await self.send(
                text_data=json.dumps({"type": "pong", "timestamp": data.get("timestamp")})
            )
        # subscribe/unsubscribe hooks — extend here for per-client filtering
        elif action in ("subscribe", "unsubscribe"):
            channels = data.get("channels", [])
            logger.debug(f"[Consumer] {action} request for channels: {channels}")

    async def market_update(self, event):
        """
        Handler invoked by Django Channels when a message is sent to the
        'market_stream' group.  Relays the inner 'message' dict to the
        WebSocket client.

        The channel type string 'market.update' maps to this method name
        by Channels convention (dots → underscores).
        """
        await self.send(text_data=json.dumps(event["message"]))
