from datetime import datetime, timedelta
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy.ext.asyncio import AsyncSession

from app.models import Event, EventType, Marathon, Challenge, Difficulty, Participant, Assignment, AssignmentStatus
from app.schemas.event import EventEffects, EVENT_INFO, COMMON_ENEMY_BONUSES
from app.services.telegram_notifier import telegram_notifier


class EventService:
    """Service for managing marathon events"""

    async def get_active_event(self, db: AsyncSession, marathon_id: int) -> Event | None:
        """Get currently active event for marathon"""
        now = datetime.utcnow()
        result = await db.execute(
            select(Event)
            .options(selectinload(Event.created_by))
            .where(
                Event.marathon_id == marathon_id,
                Event.is_active == True,
                Event.start_time <= now,
            )
            .order_by(Event.start_time.desc())
        )
        event = result.scalar_one_or_none()

        # Check if event has expired
        if event and event.end_time and event.end_time < now:
            await self.end_event(db, event.id)
            return None

        return event

    async def can_start_event(self, db: AsyncSession, marathon_id: int) -> bool:
        """Check if we can start a new event (no active event exists)"""
        active = await self.get_active_event(db, marathon_id)
        return active is None

    async def start_event(
        self,
        db: AsyncSession,
        marathon_id: int,
        event_type: str,
        created_by_id: int | None = None,
        duration_minutes: int | None = None,
        challenge_id: int | None = None,
        game_id: int | None = None,
        is_playthrough: bool = False,
    ) -> Event:
        """Start a new event"""
        # Check no active event
        if not await self.can_start_event(db, marathon_id):
            raise ValueError("An event is already active")

        # Get default duration if not provided
        event_info = EVENT_INFO.get(EventType(event_type), {})
        if duration_minutes is None:
            duration_minutes = event_info.get("default_duration")

        now = datetime.utcnow()
        end_time = now + timedelta(minutes=duration_minutes) if duration_minutes else None

        # Build event data
        data = {}
        if event_type == EventType.COMMON_ENEMY.value and (challenge_id or game_id):
            if is_playthrough and game_id:
                data["game_id"] = game_id
                data["is_playthrough"] = True
            else:
                data["challenge_id"] = challenge_id
            data["completions"] = []  # Track who completed and when

        event = Event(
            marathon_id=marathon_id,
            type=event_type,
            start_time=now,
            end_time=end_time,
            is_active=True,
            created_by_id=created_by_id,
            data=data if data else None,
        )
        db.add(event)
        await db.flush()  # Get event.id before committing

        # Auto-assign challenge/playthrough to all participants for Common Enemy
        if event_type == EventType.COMMON_ENEMY.value and (challenge_id or game_id):
            await self._assign_common_enemy_to_all(
                db, marathon_id, event.id, challenge_id, game_id, is_playthrough
            )

        await db.commit()
        await db.refresh(event)

        # Load created_by relationship
        if created_by_id:
            await db.refresh(event, ["created_by"])

        # Send Telegram notifications
        result = await db.execute(select(Marathon).where(Marathon.id == marathon_id))
        marathon = result.scalar_one_or_none()
        if marathon:
            await telegram_notifier.notify_event_start(
                db, marathon_id, event_type, marathon.title
            )

        return event

    async def _assign_common_enemy_to_all(
        self,
        db: AsyncSession,
        marathon_id: int,
        event_id: int,
        challenge_id: int | None,
        game_id: int | None = None,
        is_playthrough: bool = False,
    ) -> None:
        """Create event assignments for all participants in the marathon"""
        # Get all participants
        result = await db.execute(
            select(Participant).where(Participant.marathon_id == marathon_id)
        )
        participants = result.scalars().all()

        # Create event assignment for each participant
        for participant in participants:
            assignment = Assignment(
                participant_id=participant.id,
                challenge_id=challenge_id if not is_playthrough else None,
                game_id=game_id if is_playthrough else None,
                is_playthrough=is_playthrough,
                status=AssignmentStatus.ACTIVE.value,
                event_type=EventType.COMMON_ENEMY.value,
                is_event_assignment=True,
                event_id=event_id,
            )
            db.add(assignment)

    async def end_event(self, db: AsyncSession, event_id: int) -> None:
        """End an event and mark incomplete event assignments as expired"""
        from sqlalchemy import update

        result = await db.execute(select(Event).where(Event.id == event_id))
        event = result.scalar_one_or_none()
        if event:
            event_type = event.type
            marathon_id = event.marathon_id

            event.is_active = False
            if not event.end_time:
                event.end_time = datetime.utcnow()

            # Mark all incomplete event assignments for this event as dropped
            if event.type == EventType.COMMON_ENEMY.value:
                await db.execute(
                    update(Assignment)
                    .where(
                        Assignment.event_id == event_id,
                        Assignment.is_event_assignment == True,
                        Assignment.status == AssignmentStatus.ACTIVE.value,
                    )
                    .values(
                        status=AssignmentStatus.DROPPED.value,
                        completed_at=datetime.utcnow(),
                    )
                )

            await db.commit()

            # Send Telegram notifications about event end
            result = await db.execute(select(Marathon).where(Marathon.id == marathon_id))
            marathon = result.scalar_one_or_none()
            if marathon:
                await telegram_notifier.notify_event_end(
                    db, marathon_id, event_type, marathon.title
                )

    async def consume_jackpot(self, db: AsyncSession, event_id: int) -> None:
        """Consume jackpot event after one spin"""
        await self.end_event(db, event_id)

    def get_event_effects(self, event: Event | None) -> EventEffects:
        """Get effects of an event"""
        if not event:
            return EventEffects(description="Нет активного события")

        event_info = EVENT_INFO.get(EventType(event.type), {})

        return EventEffects(
            points_multiplier=event_info.get("points_multiplier", 1.0),
            drop_free=event_info.get("drop_free", False),
            special_action=event_info.get("special_action"),
            description=event_info.get("description", ""),
        )

    async def get_random_hard_challenge(
        self,
        db: AsyncSession,
        marathon_id: int
    ) -> Challenge | None:
        """Get a random hard challenge for jackpot event"""
        result = await db.execute(
            select(Challenge)
            .join(Challenge.game)
            .where(
                Challenge.game.has(marathon_id=marathon_id),
                Challenge.difficulty == Difficulty.HARD.value,
            )
        )
        challenges = result.scalars().all()
        if not challenges:
            # Fallback to any challenge
            result = await db.execute(
                select(Challenge)
                .join(Challenge.game)
                .where(Challenge.game.has(marathon_id=marathon_id))
            )
            challenges = result.scalars().all()

        if challenges:
            import random
            return random.choice(challenges)
        return None

    async def record_common_enemy_completion(
        self,
        db: AsyncSession,
        event: Event,
        participant_id: int,
        user_id: int,
    ) -> tuple[int, bool, list[dict] | None]:
        """
        Record completion for common enemy event.
        Returns: (bonus_points, event_closed, winners_list)
        - bonus_points: bonus for this completion (top 3 get bonuses)
        - event_closed: True if event was auto-closed (3 completions reached)
        - winners_list: list of winners if event closed, None otherwise
        """
        if event.type != EventType.COMMON_ENEMY.value:
            print(f"[COMMON_ENEMY] Event type mismatch: {event.type}")
            return 0, False, None

        data = event.data or {}
        completions = data.get("completions", [])
        print(f"[COMMON_ENEMY] Current completions count: {len(completions)}")

        # Check if already completed
        if any(c["participant_id"] == participant_id for c in completions):
            print(f"[COMMON_ENEMY] Participant {participant_id} already completed")
            return 0, False, None

        # Add completion
        rank = len(completions) + 1
        completions.append({
            "participant_id": participant_id,
            "user_id": user_id,
            "completed_at": datetime.utcnow().isoformat(),
            "rank": rank,
        })
        print(f"[COMMON_ENEMY] Added completion for user {user_id}, rank={rank}")

        # Update event data - need to flag_modified for SQLAlchemy to detect JSON changes
        event.data = {**data, "completions": completions}
        flag_modified(event, "data")

        bonus = COMMON_ENEMY_BONUSES.get(rank, 0)

        # Auto-close event when 3 players completed
        event_closed = False
        winners_list = None
        if rank >= 3:
            event.is_active = False
            event.end_time = datetime.utcnow()
            event_closed = True
            winners_list = completions[:3]  # Top 3
            print(f"[COMMON_ENEMY] Event auto-closed! Winners: {winners_list}")

        await db.commit()

        return bonus, event_closed, winners_list

    async def get_common_enemy_challenge(
        self,
        db: AsyncSession,
        event: Event
    ) -> Challenge | None:
        """Get the challenge for common enemy event"""
        if event.type != EventType.COMMON_ENEMY.value:
            return None

        data = event.data or {}
        challenge_id = data.get("challenge_id")
        if not challenge_id:
            return None

        result = await db.execute(
            select(Challenge)
            .options(selectinload(Challenge.game))
            .where(Challenge.id == challenge_id)
        )
        return result.scalar_one_or_none()

    async def get_common_enemy_game(
        self,
        db: AsyncSession,
        event: Event
    ):
        """Get the playthrough game for common enemy event (if it's a playthrough)"""
        from app.models import Game

        if event.type != EventType.COMMON_ENEMY.value:
            return None

        data = event.data or {}
        if not data.get("is_playthrough"):
            return None

        game_id = data.get("game_id")
        if not game_id:
            return None

        result = await db.execute(
            select(Game).where(Game.id == game_id)
        )
        return result.scalar_one_or_none()

    def get_time_remaining(self, event: Event | None) -> int | None:
        """Get remaining time in seconds for an event"""
        if not event or not event.end_time:
            return None

        remaining = (event.end_time - datetime.utcnow()).total_seconds()
        return max(0, int(remaining))


event_service = EventService()
