from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer


LEVITY_ROLES = {"PC", "NC", "SC"}
GRAVITY_ROLES = {"BC", "EC", "AC"}


class RoomConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        self.room_id = self.scope["url_route"]["kwargs"]["room_id"]
        self.group_name = f"room_{self.room_id}"
        await self.channel_layer.group_add(self.group_name, self.channel_name)

        self.cursor_group = None
        user = self.scope.get("user")
        if user and user.is_authenticated:
            # CARTE owners hold seats in both polarities; subscribe to the
            # cursor group of the seat they're ACTING AS (the ?seat carried on
            # the WS URL), so the per-polarity countdown reaches the right room.
            seat = await self._get_seat(user, self._seat_param())
            if seat:
                if seat.role in LEVITY_ROLES:
                    self.cursor_group = f"cursors_{self.room_id}_levity"
                elif seat.role in GRAVITY_ROLES:
                    self.cursor_group = f"cursors_{self.room_id}_gravity"
                if self.cursor_group:
                    await self.channel_layer.group_add(self.cursor_group, self.channel_name)

        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(self.group_name, self.channel_name)
        if self.cursor_group:
            await self.channel_layer.group_discard(self.cursor_group, self.channel_name)

    async def receive_json(self, content):
        msg_type = content.get("type")
        if msg_type == "cursor_move" and self.cursor_group:
            await self.channel_layer.group_send(
                self.cursor_group,
                {"type": "cursor_move", "x": content.get("x"), "y": content.get("y")},
            )
        elif msg_type == "sig_hover" and self.cursor_group:
            await self.channel_layer.group_send(
                self.cursor_group,
                {
                    "type": "sig_hover",
                    "card_id": content.get("card_id"),
                    "role": content.get("role"),
                    "active": content.get("active"),
                },
            )

    def _seat_param(self):
        """The ?seat=N value off the WS query string, or None."""
        from urllib.parse import parse_qs
        qs = parse_qs(self.scope.get("query_string", b"").decode())
        values = qs.get("seat")
        return values[0] if values else None

    @database_sync_to_async
    def _get_seat(self, user, seat_param=None):
        """The seat the viewer is acting as: the ?seat owned-slot override, else
        the role-canonical seat (PC→NC→EC→SC→AC→BC first) — matching the
        overlay's seat resolution so the WS cursor group agrees with the page."""
        from apps.epic.models import TableSeat
        seats = list(TableSeat.objects.filter(room_id=self.room_id, gamer=user))
        if not seats:
            return None
        if seat_param:
            try:
                n = int(seat_param)
            except (TypeError, ValueError):
                n = None
            if n is not None:
                override = next((s for s in seats if s.slot_number == n), None)
                if override:
                    return override
        order = {"PC": 0, "NC": 1, "EC": 2, "SC": 3, "AC": 4, "BC": 5}
        return min(seats, key=lambda s: order.get(s.role, 99))

    async def gate_update(self, event):
        await self.send_json(event)

    async def role_select_start(self, event):
        await self.send_json(event)

    async def turn_changed(self, event):
        await self.send_json(event)

    async def all_roles_filled(self, event):
        await self.send_json(event)

    async def sig_select_started(self, event):
        await self.send_json(event)

    async def sig_selected(self, event):
        await self.send_json(event)

    async def sig_hover(self, event):
        await self.send_json(event)

    async def sig_reserved(self, event):
        await self.send_json(event)

    async def countdown_start(self, event):
        await self.send_json(event)

    async def countdown_cancel(self, event):
        await self.send_json(event)

    async def polarity_room_done(self, event):
        await self.send_json(event)

    async def pick_sky_available(self, event):
        await self.send_json(event)

    async def sky_confirmed(self, event):
        await self.send_json(event)

    async def cursor_move(self, event):
        await self.send_json(event)

    async def scroll_update(self, event):
        await self.send_json(event)
