from decimal import Decimal
from rest_framework import serializers
from django.utils import timezone

from .models import (
    LoyaltyProgram, LoyaltyRule, CustomerWallet, CoinTransaction,
    LoyaltySubscription, WalletTopup,
    KhataAccount, KhataTransaction, LoyaltyGroup, LoyaltyGroupMember,
    DeadHourDrop,
)
from .loyalty_services import WalletService, IronLawsEnforcer, IronLawsError


class LoyaltyRuleSerializer(serializers.ModelSerializer):
    rule_type_display = serializers.CharField(source='get_rule_type_display', read_only=True)

    class Meta:
        model = LoyaltyRule
        fields = [
            'id', 'rule_type', 'rule_type_display', 'is_active',
            'title', 'description', 'trigger_config',
            'reward_coins', 'reward_expiry_hours',
            'created_at', 'updated_at',
        ]
        read_only_fields = ['created_at', 'updated_at']


class LoyaltyProgramSerializer(serializers.ModelSerializer):
    rules = LoyaltyRuleSerializer(many=True, read_only=True)
    restaurant_name = serializers.CharField(source='restaurant.name', read_only=True)

    class Meta:
        model = LoyaltyProgram
        fields = [
            'id', 'restaurant', 'restaurant_name', 'program_name',
            'is_active', 'coin_value_in_rupees', 'redemption_cap_percent',
            'rules', 'created_at', 'updated_at',
        ]
        read_only_fields = ['restaurant', 'created_at', 'updated_at']


class LoyaltyProgramSetupSerializer(serializers.ModelSerializer):
    """Used by owner to create or update the program config."""
    class Meta:
        model = LoyaltyProgram
        fields = [
            'program_name', 'is_active',
            'coin_value_in_rupees', 'redemption_cap_percent',
        ]


class CoinTransactionSerializer(serializers.ModelSerializer):
    transaction_type_display = serializers.CharField(
        source='get_transaction_type_display', read_only=True
    )
    expires_in_hours = serializers.SerializerMethodField()

    class Meta:
        model = CoinTransaction
        fields = [
            'id', 'transaction_type', 'transaction_type_display',
            'coins', 'notes', 'expires_at', 'expires_in_hours',
            'is_expired', 'created_at',
        ]

    def get_expires_in_hours(self, obj):
        if not obj.expires_at or obj.is_expired:
            return None
        delta = obj.expires_at - timezone.now()
        hours = delta.total_seconds() / 3600
        return round(hours, 1) if hours > 0 else 0


class CustomerWalletSerializer(serializers.ModelSerializer):
    restaurant_name = serializers.CharField(source='restaurant.name', read_only=True)
    coin_value_in_rupees = serializers.SerializerMethodField()
    balance_in_rupees = serializers.SerializerMethodField()
    expiring_soon = serializers.SerializerMethodField()
    recent_transactions = serializers.SerializerMethodField()

    class Meta:
        model = CustomerWallet
        fields = [
            'id', 'restaurant', 'restaurant_name',
            'coin_balance', 'coin_value_in_rupees', 'balance_in_rupees',
            'lifetime_cash_spent', 'total_orders',
            'referral_code', 'is_summit_vip', 'summit_vip_at',
            'expiring_soon', 'recent_transactions',
            'created_at',
        ]

    def get_coin_value_in_rupees(self, obj):
        try:
            return str(obj.restaurant.loyalty_program.coin_value_in_rupees)
        except LoyaltyProgram.DoesNotExist:
            return '1.00'

    def get_balance_in_rupees(self, obj):
        try:
            val = obj.coin_balance * obj.restaurant.loyalty_program.coin_value_in_rupees
            return str(round(val, 2))
        except LoyaltyProgram.DoesNotExist:
            return str(obj.coin_balance)

    def get_expiring_soon(self, obj):
        """Coins expiring in next 48 hours."""
        cutoff = timezone.now() + timezone.timedelta(hours=48)
        txs = obj.transactions.filter(
            is_expired=False,
            expires_at__isnull=False,
            expires_at__lte=cutoff,
            coins__gt=0,
        )
        total = sum(tx.coins for tx in txs)
        return {
            'coins': str(total),
            'cutoff': cutoff.isoformat(),
        }

    def get_recent_transactions(self, obj):
        txs = obj.transactions.order_by('-created_at')[:10]
        return CoinTransactionSerializer(txs, many=True).data


class RedemptionRequestSerializer(serializers.Serializer):
    booking_id = serializers.IntegerField()
    coins_to_redeem = serializers.DecimalField(max_digits=10, decimal_places=2)

    def validate_coins_to_redeem(self, value):
        if value <= 0:
            raise serializers.ValidationError("Must redeem at least 1 coin.")
        return value


class RedemptionResponseSerializer(serializers.Serializer):
    coins_redeemed = serializers.DecimalField(max_digits=10, decimal_places=2)
    discount_amount = serializers.DecimalField(max_digits=10, decimal_places=2)
    new_balance = serializers.DecimalField(max_digits=12, decimal_places=2)
    message = serializers.CharField()


class ReferralApplySerializer(serializers.Serializer):
    referral_code = serializers.CharField(max_length=20)
    restaurant_id = serializers.IntegerField()

    def validate(self, data):
        from .models import Restaurant
        try:
            restaurant = Restaurant.objects.get(pk=data['restaurant_id'])
        except Restaurant.DoesNotExist:
            raise serializers.ValidationError("Restaurant not found.")
        try:
            referrer_wallet = CustomerWallet.objects.get(
                referral_code=data['referral_code'],
                restaurant=restaurant,
            )
        except CustomerWallet.DoesNotExist:
            raise serializers.ValidationError("Invalid referral code.")
        data['referrer_wallet'] = referrer_wallet
        data['restaurant'] = restaurant
        return data


class LoyaltyAnalyticsSerializer(serializers.Serializer):
    total_wallets = serializers.IntegerField()
    active_wallets_30d = serializers.IntegerField()
    coins_issued_total = serializers.DecimalField(max_digits=14, decimal_places=2)
    coins_redeemed_total = serializers.DecimalField(max_digits=14, decimal_places=2)
    redemption_rate_percent = serializers.FloatField()
    summit_vips = serializers.IntegerField()
    coins_expiring_48h = serializers.DecimalField(max_digits=14, decimal_places=2)


# ──────────────────────────────────────────────
# Phase 2 — Subscription + Wallet Top-up
# ──────────────────────────────────────────────

class LoyaltySubscriptionSerializer(serializers.ModelSerializer):
    status_display = serializers.CharField(source='get_status_display', read_only=True)
    days_remaining = serializers.SerializerMethodField()

    class Meta:
        model = LoyaltySubscription
        fields = [
            'id', 'amount_paid', 'payment_reference',
            'valid_from', 'valid_until', 'status', 'status_display',
            'last_used_date', 'total_uses', 'days_remaining', 'created_at',
        ]

    def get_days_remaining(self, obj):
        today = timezone.now().date()
        if obj.status != 'ACTIVE' or obj.valid_until < today:
            return 0
        return (obj.valid_until - today).days


class SubscriptionCreateSerializer(serializers.Serializer):
    rule_id = serializers.IntegerField()
    payment_reference = serializers.CharField(max_length=100, allow_blank=True, default='')

    def validate_rule_id(self, value):
        try:
            rule = LoyaltyRule.objects.get(pk=value, rule_type='SUBSCRIPTION', is_active=True)
        except LoyaltyRule.DoesNotExist:
            raise serializers.ValidationError("Active subscription rule not found.")
        return rule


class WalletTopupSerializer(serializers.ModelSerializer):
    class Meta:
        model = WalletTopup
        fields = ['id', 'cash_paid', 'coins_credited', 'payment_reference', 'created_at']


class TopupRequestSerializer(serializers.Serializer):
    rule_id = serializers.IntegerField()
    cash_amount = serializers.DecimalField(max_digits=10, decimal_places=2)
    payment_reference = serializers.CharField(max_length=100, allow_blank=True, default='')

    def validate(self, data):
        try:
            rule = LoyaltyRule.objects.get(pk=data['rule_id'], rule_type='WALLET_TOPUP', is_active=True)
        except LoyaltyRule.DoesNotExist:
            raise serializers.ValidationError("Active wallet top-up rule not found.")
        data['rule'] = rule
        return data


# ──────────────────────────────────────────────
# Phase 3 — Khata Account
# ──────────────────────────────────────────────

class KhataTransactionSerializer(serializers.ModelSerializer):
    class Meta:
        model = KhataTransaction
        fields = ['id', 'amount', 'is_cleared', 'cleared_at', 'created_at']


class KhataAccountSerializer(serializers.ModelSerializer):
    status_display = serializers.CharField(source='get_status_display', read_only=True)
    lifelines_remaining = serializers.IntegerField(read_only=True)
    pending_transactions = serializers.SerializerMethodField()

    class Meta:
        model = KhataAccount
        fields = [
            'id', 'lifelines_total', 'lifelines_used', 'lifelines_remaining',
            'current_tab_amount', 'tab_due_date', 'status', 'status_display',
            'pending_transactions', 'created_at',
        ]

    def get_pending_transactions(self, obj):
        txs = obj.khata_transactions.filter(is_cleared=False)
        return KhataTransactionSerializer(txs, many=True).data


class KhataUseLifelineSerializer(serializers.Serializer):
    booking_id = serializers.IntegerField()


class KhataClearSerializer(serializers.Serializer):
    payment_reference = serializers.CharField(max_length=100, allow_blank=True, default='')


# ──────────────────────────────────────────────
# Phase 3 — Society Vault / Groups
# ──────────────────────────────────────────────

class LoyaltyGroupMemberSerializer(serializers.ModelSerializer):
    username = serializers.CharField(source='wallet.customer.username', read_only=True)

    class Meta:
        model = LoyaltyGroupMember
        fields = ['id', 'username', 'joined_at']


class LoyaltyGroupSerializer(serializers.ModelSerializer):
    member_count = serializers.SerializerMethodField()

    class Meta:
        model = LoyaltyGroup
        fields = [
            'id', 'group_name', 'vault_balance', 'vault_percentage',
            'semester_start', 'semester_end', 'is_active',
            'member_count', 'created_at',
        ]
        read_only_fields = ['vault_balance', 'created_at']

    def get_member_count(self, obj):
        return obj.members.count()


class GroupCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = LoyaltyGroup
        fields = ['group_name', 'vault_percentage', 'semester_start', 'semester_end']


class JoinGroupSerializer(serializers.Serializer):
    group_id = serializers.IntegerField()
    restaurant_id = serializers.IntegerField()


class VaultRedeemSerializer(serializers.Serializer):
    amount = serializers.DecimalField(max_digits=10, decimal_places=2)
    notes = serializers.CharField(max_length=255, allow_blank=True, default='')


# ──────────────────────────────────────────────
# Phase 4 — Dead-Hour Drops + Full Analytics
# ──────────────────────────────────────────────

class DeadHourDropSerializer(serializers.ModelSerializer):
    class Meta:
        model = DeadHourDrop
        fields = ['id', 'message', 'discount_code', 'valid_until', 'total_recipients', 'created_at']
        read_only_fields = ['total_recipients', 'created_at']


class DeadHourDropCreateSerializer(serializers.Serializer):
    message = serializers.CharField(max_length=500)
    discount_code = serializers.CharField(max_length=50, allow_blank=True, default='')
    valid_hours = serializers.IntegerField(min_value=1, max_value=72, default=2)


class FullAnalyticsSerializer(serializers.Serializer):
    total_wallets = serializers.IntegerField()
    active_wallets_30d = serializers.IntegerField()
    coins_issued_total = serializers.DecimalField(max_digits=14, decimal_places=2)
    coins_redeemed_total = serializers.DecimalField(max_digits=14, decimal_places=2)
    redemption_rate_percent = serializers.FloatField()
    avg_coins_per_customer = serializers.FloatField()
    summit_vips = serializers.IntegerField()
    coins_expiring_24h = serializers.DecimalField(max_digits=14, decimal_places=2)
    revenue_influenced = serializers.DecimalField(max_digits=14, decimal_places=2)
    top_10_customers = serializers.ListField(child=serializers.DictField())
