"""
Coins Service - handles all coin-related operations

Coins are earned only in certified marathons and can be spent in the shop.
"""
from sqlalchemy.ext.asyncio import AsyncSession

from app.models import User, Participant, Marathon, CoinTransaction, CoinTransactionType
from app.models.challenge import Difficulty


class CoinsService:
    """Service for managing coin transactions and balances"""

    # Coins awarded per challenge difficulty (only in certified marathons)
    CHALLENGE_COINS = {
        Difficulty.EASY.value: 10,
        Difficulty.MEDIUM.value: 20,
        Difficulty.HARD.value: 35,
    }

    # Coins for playthrough = points * this ratio
    PLAYTHROUGH_COIN_RATIO = 0.10  # 10% of points

    # Coins awarded for marathon placements
    MARATHON_PLACE_COINS = {
        1: 500,  # 1st place
        2: 250,  # 2nd place
        3: 150,  # 3rd place
    }

    # Bonus coins for Common Enemy event winners
    COMMON_ENEMY_BONUS_COINS = {
        1: 15,  # First to complete
        2: 10,  # Second
        3: 5,   # Third
    }

    async def award_challenge_coins(
        self,
        db: AsyncSession,
        user: User,
        participant: Participant,
        marathon: Marathon,
        difficulty: str,
        assignment_id: int,
    ) -> int:
        """
        Award coins for completing a challenge.
        Only awards coins if marathon is certified.

        Returns: number of coins awarded (0 if marathon not certified)
        """
        if not marathon.is_certified:
            return 0

        coins = self.CHALLENGE_COINS.get(difficulty, 0)
        if coins <= 0:
            return 0

        # Create transaction
        transaction = CoinTransaction(
            user_id=user.id,
            amount=coins,
            transaction_type=CoinTransactionType.CHALLENGE_COMPLETE.value,
            reference_type="assignment",
            reference_id=assignment_id,
            description=f"Challenge completion ({difficulty})",
        )
        db.add(transaction)

        # Update balances
        user.coins_balance += coins
        participant.coins_earned += coins

        return coins

    async def award_playthrough_coins(
        self,
        db: AsyncSession,
        user: User,
        participant: Participant,
        marathon: Marathon,
        points: int,
        assignment_id: int,
    ) -> int:
        """
        Award coins for completing a playthrough.
        Coins = points * PLAYTHROUGH_COIN_RATIO

        Returns: number of coins awarded (0 if marathon not certified)
        """
        if not marathon.is_certified:
            return 0

        coins = int(points * self.PLAYTHROUGH_COIN_RATIO)
        if coins <= 0:
            return 0

        transaction = CoinTransaction(
            user_id=user.id,
            amount=coins,
            transaction_type=CoinTransactionType.PLAYTHROUGH_COMPLETE.value,
            reference_type="assignment",
            reference_id=assignment_id,
            description=f"Playthrough completion ({points} points)",
        )
        db.add(transaction)

        user.coins_balance += coins
        participant.coins_earned += coins

        return coins

    async def award_marathon_place(
        self,
        db: AsyncSession,
        user: User,
        marathon: Marathon,
        place: int,
    ) -> int:
        """
        Award coins for placing in a marathon (1st, 2nd, 3rd).

        Returns: number of coins awarded (0 if not top 3 or not certified)
        """
        if not marathon.is_certified:
            return 0

        coins = self.MARATHON_PLACE_COINS.get(place, 0)
        if coins <= 0:
            return 0

        transaction = CoinTransaction(
            user_id=user.id,
            amount=coins,
            transaction_type=CoinTransactionType.MARATHON_PLACE.value,
            reference_type="marathon",
            reference_id=marathon.id,
            description=f"Marathon #{place} place: {marathon.title}",
        )
        db.add(transaction)

        user.coins_balance += coins

        return coins

    async def award_common_enemy_bonus(
        self,
        db: AsyncSession,
        user: User,
        participant: Participant,
        marathon: Marathon,
        rank: int,
        event_id: int,
    ) -> int:
        """
        Award bonus coins for Common Enemy event completion.

        Returns: number of bonus coins awarded
        """
        if not marathon.is_certified:
            return 0

        coins = self.COMMON_ENEMY_BONUS_COINS.get(rank, 0)
        if coins <= 0:
            return 0

        transaction = CoinTransaction(
            user_id=user.id,
            amount=coins,
            transaction_type=CoinTransactionType.COMMON_ENEMY_BONUS.value,
            reference_type="event",
            reference_id=event_id,
            description=f"Common Enemy #{rank} place",
        )
        db.add(transaction)

        user.coins_balance += coins
        participant.coins_earned += coins

        return coins

    async def spend_coins(
        self,
        db: AsyncSession,
        user: User,
        amount: int,
        description: str,
        reference_type: str | None = None,
        reference_id: int | None = None,
    ) -> bool:
        """
        Spend coins (for purchases).

        Returns: True if successful, False if insufficient balance
        """
        if user.coins_balance < amount:
            return False

        transaction = CoinTransaction(
            user_id=user.id,
            amount=-amount,  # Negative for spending
            transaction_type=CoinTransactionType.PURCHASE.value,
            reference_type=reference_type,
            reference_id=reference_id,
            description=description,
        )
        db.add(transaction)

        user.coins_balance -= amount
        return True

    async def refund_coins(
        self,
        db: AsyncSession,
        user: User,
        amount: int,
        description: str,
        reference_type: str | None = None,
        reference_id: int | None = None,
    ) -> None:
        """Refund coins to user (for failed purchases, etc.)"""
        transaction = CoinTransaction(
            user_id=user.id,
            amount=amount,
            transaction_type=CoinTransactionType.REFUND.value,
            reference_type=reference_type,
            reference_id=reference_id,
            description=description,
        )
        db.add(transaction)

        user.coins_balance += amount

    async def admin_grant_coins(
        self,
        db: AsyncSession,
        user: User,
        amount: int,
        reason: str,
        admin_id: int,
    ) -> None:
        """Admin grants coins to user"""
        transaction = CoinTransaction(
            user_id=user.id,
            amount=amount,
            transaction_type=CoinTransactionType.ADMIN_GRANT.value,
            reference_type="admin",
            reference_id=admin_id,
            description=f"Admin grant: {reason}",
        )
        db.add(transaction)

        user.coins_balance += amount

    async def admin_deduct_coins(
        self,
        db: AsyncSession,
        user: User,
        amount: int,
        reason: str,
        admin_id: int,
    ) -> bool:
        """
        Admin deducts coins from user.

        Returns: True if successful, False if insufficient balance
        """
        if user.coins_balance < amount:
            return False

        transaction = CoinTransaction(
            user_id=user.id,
            amount=-amount,
            transaction_type=CoinTransactionType.ADMIN_DEDUCT.value,
            reference_type="admin",
            reference_id=admin_id,
            description=f"Admin deduction: {reason}",
        )
        db.add(transaction)

        user.coins_balance -= amount
        return True


# Singleton instance
coins_service = CoinsService()
