from rest_framework import serializers
from django.db.models import Q
from decimal import Decimal, ROUND_HALF_UP
from .models import Order
from apps.business.schemes.services.distribution_service import resolve_distribution_base_amount
from apps.business.schemes.models import OpportunityBundleDistribution


class OrderSerializer(serializers.ModelSerializer):
    product_name = serializers.CharField(source='product.name', read_only=True)
    product_hsn_code = serializers.CharField(source='product.hsn_code', read_only=True, default=None)
    product_gst_percentage = serializers.DecimalField(
        source='product.gst_percentage', max_digits=6, decimal_places=3, read_only=True, default=None
    )
    product_base_cost = serializers.DecimalField(
        source='product.base_cost', max_digits=12, decimal_places=2, read_only=True, default=None
    )
    product_category = serializers.CharField(source='product.category', read_only=True, default=None)
    distributor_code = serializers.CharField(source='distributor.distributor_code', read_only=True)
    distributor_id = serializers.UUIDField(source='distributor.id', read_only=True)
    delivery_store_name = serializers.CharField(source='delivery_store.name', read_only=True)
    franchise_user_id = serializers.UUIDField(source='franchise_assignment.user_id', read_only=True)
    opportunity_bundle_name = serializers.CharField(source='product.opportunity_bundle.name', read_only=True)
    distribution_split = serializers.SerializerMethodField()

    def get_distribution_split(self, obj):
        product = getattr(obj, 'product', None)
        if not product or not getattr(product, 'opportunity_bundle_id', None):
            return []

        try:
            base_amount = resolve_distribution_base_amount(product, obj.amount)
            distributions = OpportunityBundleDistribution.objects.filter(opportunity_bundle_id=product.opportunity_bundle_id).order_by('component_type')
            breakdown = []

            for dist in distributions:
                if dist.value_type == OpportunityBundleDistribution.ValueType.FIXED:
                    amount = Decimal(str(dist.value or 0))
                elif dist.value_type == OpportunityBundleDistribution.ValueType.PERCENT:
                    amount = Decimal(str(base_amount or 0)) * Decimal(str(dist.value or 0)) / Decimal('100')
                else:
                    amount = Decimal('0')

                breakdown.append({
                    'type': dist.component_type,
                    'value_type': dist.value_type,
                    'amount': amount.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP),
                })

            return [
                {
                    'type': entry.get('type'),
                    'value_type': entry.get('value_type'),
                    'amount': str(entry.get('amount')),
                }
                for entry in breakdown
            ]
        except Exception:
            return []
    
    class Meta:
        model = Order
        fields = [
            'id',
            'reference_id',
            'product_name',
            'product_hsn_code',
            'product_gst_percentage',
            'product_base_cost',
            'product_category',
            'amount',
            'voucher_amount',
            'super_coin_amount',
            'cash_amount',
            'status',
            'payment_method',
            'qr_reference',
            'distributor_id',
            'distributor_code',
            'franchise_assignment',
            'franchise_user_id',
            'delivery_store',
            'delivery_store_name',
            'fulfillment_mode',
            'franchise_fulfillment_status',
            'opportunity_bundle_name',
            'distribution_split',
            'billing_address',
            'shipping_address',
            'dispatch_address',
            'buyer_gst_number',
            'buyer_company_name',
            'place_of_supply',
            'payment_mode',
            'ownership_status',
            'is_availability_blocked',
            'part_payment_percentage',
            'part_total_amount',
            'part_paid_amount',
            'part_pending_amount',
            'part_payment_started_at',
            'part_payment_due_at',
            'part_payment_completed_at',
            'part_payment_converted_at',
            'part_payment_conversion_product',
            'part_payment_conversion_notes',
            'created_at',
            'updated_at',
        ]
        read_only_fields = ['id', 'reference_id', 'created_at', 'updated_at']


# Serializer for purchase API request
from apps.business.products.models import Product
from apps.business.distributor.models import DistributorID
from apps.accounts.models import User


class PurchaseRequestSerializer(serializers.Serializer):
    product_id = serializers.UUIDField()
    sponsor_distributor_id = serializers.UUIDField(required=False, allow_null=True)
    referrer_distributor_id = serializers.UUIDField(required=False, allow_null=True)
    distributor_type = serializers.ChoiceField(choices=['own', 'referrer'], required=False)
    referral_id = serializers.CharField(required=False, allow_blank=True, max_length=64)
    client_reference_id = serializers.CharField(max_length=128)
    use_voucher = serializers.BooleanField(required=False, default=False)
    payment_method = serializers.ChoiceField(
        choices=['main_wallet', 'repurchase_wallet', 'super_coins', 'razorpay', 'qr_scan', 'bank_transfer', 'phonepe', 'cash_payment'],
        default='main_wallet',
        required=False
    )
    use_super_coins = serializers.BooleanField(required=False, default=False)
    super_coin_amount = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
    qr_reference = serializers.CharField(max_length=128, required=False, allow_blank=True)
    repeat_purchase_confirmed = serializers.BooleanField(required=False, default=False)
    payment_mode = serializers.ChoiceField(choices=['FULL', 'PART'], default='FULL', required=False)

    def validate_product_id(self, value):
        if not Product.objects.filter(id=value, is_active=True).exists():
            raise serializers.ValidationError("Invalid or inactive product.")
        return value

    def validate_sponsor_distributor_id(self, value):
        if value is not None and not DistributorID.objects.filter(id=value, is_active=True).exists():
            raise serializers.ValidationError("Invalid sponsor distributor.")
        return value

    def validate_referrer_distributor_id(self, value):
        if value is not None and not DistributorID.objects.filter(id=value, is_active=True).exists():
            raise serializers.ValidationError("Invalid referrer distributor.")
        return value

    def _resolve_referral_user(self, referral_id):
        referral_id = (referral_id or '').strip()
        if not referral_id:
            return None

        user_qs = User.objects.filter(
            Q(referral_code__iexact=referral_id) |
            Q(mobile__iexact=referral_id)
        )
        if referral_id.isdigit():
            user_qs = user_qs | User.objects.filter(id=referral_id)
        return user_qs.first()

    def _validate_selected_distributor(self, *, user, product, sponsor_distributor, distributor_type, referral_id=''):
        # Any valid active distributor can be used as sponsor regardless of ownership.
        selected_scheme_id = getattr(sponsor_distributor.product, 'opportunity_bundle_id', None)
        product_scheme_id = getattr(product, 'opportunity_bundle_id', None)

        if product_scheme_id:
            if selected_scheme_id != product_scheme_id:
                raise serializers.ValidationError({
                    'sponsor_distributor_id': 'Selected distributor ID does not belong to the same opportunity_bundle as the chosen product.'
                })
        elif sponsor_distributor.product_id != product.id:
            raise serializers.ValidationError({
                'sponsor_distributor_id': 'Selected distributor ID does not belong to the chosen product.'
            })

    def validate(self, data):
        user = self.context.get('request').user
        if not user or not user.is_authenticated:
            raise serializers.ValidationError("Authentication required.")

        product = Product.objects.select_related('opportunity_bundle').filter(id=data['product_id'], is_active=True).first()
        if not product:
            raise serializers.ValidationError({'product_id': 'Invalid or inactive product.'})

        selected_ids = {
            selected_id
            for selected_id in [
                data.get('sponsor_distributor_id'),
                data.get('referrer_distributor_id'),
            ]
            if selected_id is not None
        }

        if len(selected_ids) > 1:
            raise serializers.ValidationError(
                'Provide only one distributor selection for the purchase.'
            )

        selected_id = next(iter(selected_ids), None)
        if selected_id is not None:
            sponsor_distributor = DistributorID.objects.select_related('product__opportunity_bundle').filter(
                id=selected_id,
                is_active=True,
            ).first()
            if not sponsor_distributor:
                raise serializers.ValidationError({'sponsor_distributor_id': 'Invalid sponsor distributor.'})

            distributor_type = data.get('distributor_type') or 'referrer'

            self._validate_selected_distributor(
                user=user,
                product=product,
                sponsor_distributor=sponsor_distributor,
                distributor_type=distributor_type,
                referral_id=data.get('referral_id') or '',
            )

            data['sponsor_distributor_id'] = sponsor_distributor.id
            data['selected_sponsor_distributor'] = sponsor_distributor
            data['distributor_type'] = distributor_type
        
        # Prevent duplicate distributor for same user/product/client_reference_id
        if Order.objects.filter(user=user, product_id=data['product_id'], client_reference_id=data['client_reference_id']).exists():
            raise serializers.ValidationError("Duplicate purchase: this client_reference_id has already been used.")
        
        # Transaction reference is only required for UPI/bank style methods.
        if data.get('payment_method') in {'qr_scan', 'bank_transfer', 'phonepe'} and not data.get('qr_reference'):
            raise serializers.ValidationError("Transaction reference is required for this payment method.")

        payment_mode = (data.get('payment_mode') or 'FULL').upper()
        if payment_mode == 'PART':
            if not getattr(product, 'opportunity_bundle_id', None):
                raise serializers.ValidationError({'payment_mode': 'Part payment is only supported for bundle-linked products.'})
            if not getattr(product, 'allow_part_payment', False):
                raise serializers.ValidationError({'payment_mode': 'Part payment is not enabled for this product.'})
            if data.get('use_voucher'):
                raise serializers.ValidationError({'use_voucher': 'TWM coins/voucher usage is not allowed for part payment.'})
            if data.get('payment_method') == 'repurchase_wallet':
                raise serializers.ValidationError({'payment_method': 'Repurchase wallet is not allowed for part payment.'})

        if data.get('super_coin_amount') is not None and Decimal(str(data.get('super_coin_amount'))) < Decimal('0'):
            raise serializers.ValidationError({'super_coin_amount': 'Super coin amount cannot be negative.'})
        
        return data
