import uuid
from decimal import Decimal
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.models import Order, Referral, User
from app.repositories import ReferralRepository, UserRepository
from app.services.log_service import LogService, LogType
from app.services.settings_service import SettingsService
from app.services.wallet_service import TransactionType, WalletService
from app.utils.helpers import money


class ReferralService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.referrals = ReferralRepository(session)
        self.users = UserRepository(session)
        self.wallets = WalletService(session)
        self.settings = SettingsService(session)
        self.logs = LogService(session)

    async def reward_for_order(self, order: Order) -> Referral | None:
        """Credit the referrer once, right after an order completes."""
        if order.flow == "topup":
            return None

        user = await self.users.get(order.user_id)
        if not user or not user.referred_by:
            return None

        existing = await self.referrals.get_by(order_id=order.id)
        if existing:
            return existing

        percent = await self.settings.get_decimal("referral_percent")
        if percent <= 0:
            return None

        reward = money(Decimal(order.final_price) * percent / Decimal("100"))
        if reward <= 0:
            return None

        referral = await self.referrals.create(
            referrer_user_id=user.referred_by,
            referred_user_id=user.id,
            order_id=order.id,
            reward=reward,
            status="paid",
        )
        await self.wallets.deposit(
            user.referred_by,
            reward,
            description=f"referral reward {order.order_number}",
            order_id=order.id,
            reference=f"referral:{order.id}",
            tx_type=TransactionType.BONUS,
        )
        await self.logs.write(
            LogType.WALLET,
            "referral_reward",
            f"referral reward for order {order.order_number}",
            user_id=user.referred_by,
            order_id=order.id,
            data={"reward": str(reward)},
        )
        return referral

    async def statistics(self, user: User) -> dict[str, Any]:
        referrals = await self.referrals.list_for_user(user.id, limit=200)
        invited = await self.users.count_referrals(user.id)
        total = sum((Decimal(item.reward) for item in referrals), Decimal("0"))
        return {
            "referral_code": user.referral_code,
            "invited_count": invited,
            "orders_count": len(referrals),
            "total_reward": str(money(total)),
            "percent": str(await self.settings.get_decimal("referral_percent")),
        }

    async def search(self, page: int, size: int, **filters: Any) -> tuple[list[Referral], int]:
        return await self.referrals.search(page, size, **filters)

    async def list_for_user(self, user_id: uuid.UUID, limit: int = 50) -> list[Referral]:
        return await self.referrals.list_for_user(user_id, limit)
