"""
DRF serializers for the accounting app.
"""

from rest_framework import serializers
from django.contrib.auth.models import User
from django.utils import timezone

from .models import StaffProfile, AccountHead, AccountTransaction, Receipt
from members.models import Member


# ---------------------------------------------------------------------------
# StaffProfile
# ---------------------------------------------------------------------------

class StaffProfileSerializer(serializers.ModelSerializer):
    username = serializers.CharField(source='user.username', read_only=True)
    is_member = serializers.SerializerMethodField()
    member_id = serializers.SerializerMethodField()
    member_no = serializers.SerializerMethodField()

    class Meta:
        model = StaffProfile
        fields = [
            'id', 'username', 'name', 'name_ta', 'phone', 'role',
            'is_active', 'is_member', 'member_id', 'member_no', 'created_at', 'updated_at',
        ]
        read_only_fields = ['created_at', 'updated_at']

    def get_is_member(self, obj) -> bool:
        return hasattr(obj.user, 'member')

    def get_member_id(self, obj):
        if hasattr(obj.user, 'member'):
            return obj.user.member.id
        return None

    def get_member_no(self, obj):
        if hasattr(obj.user, 'member'):
            return obj.user.member.member_id
        return None


class StaffProfileCreateSerializer(serializers.Serializer):
    """Create a Django User + StaffProfile, or assign StaffProfile to an existing Member."""

    name = serializers.CharField(max_length=200, required=False, default='')
    name_ta = serializers.CharField(max_length=200, allow_blank=True, default='')
    phone = serializers.CharField(max_length=15, required=False, default='')
    password = serializers.CharField(write_only=True, min_length=8, required=False, allow_blank=True, default='')
    role = serializers.ChoiceField(
        choices=StaffProfile.ROLE_CHOICES, default='ACCOUNTANT',
    )
    member_id = serializers.IntegerField(required=False, allow_null=True)

    def validate(self, data):
        import re
        member_id = data.get('member_id')
        if member_id:
            try:
                member = Member.objects.get(id=member_id)
                data['member_obj'] = member
            except Member.DoesNotExist:
                raise serializers.ValidationError("Selected member does not exist.")
            
            # Check if this user already has an active StaffProfile
            if StaffProfile.objects.filter(user=member.user, is_active=True).exists():
                raise serializers.ValidationError("This member is already an active accountant.")
        else:
            name = data.get('name', '').strip()
            phone = data.get('phone', '').strip().replace(' ', '')
            password = data.get('password', '')

            if not name or not phone or not password:
                raise serializers.ValidationError("Name, phone, and password are required when adding an external accountant.")

            if phone.startswith('+91'):
                phone = phone[3:]
            elif phone.startswith('91') and len(phone) == 12:
                phone = phone[2:]
            if not re.match(r'^\d{10}$', phone):
                raise serializers.ValidationError("Phone number must be exactly 10 digits.")
            if User.objects.filter(username=phone).exists():
                raise serializers.ValidationError("A user with this phone number already exists.")
            if StaffProfile.objects.filter(phone=phone).exists():
                raise serializers.ValidationError("A staff profile with this phone number already exists.")
            data['phone'] = phone

        return data

    def create(self, validated_data):
        member_obj = validated_data.get('member_obj')
        if member_obj:
            user = member_obj.user
            safe_phone = member_obj.phone or getattr(user, 'username', '') or f"M_{member_obj.id}"
            profile, created = StaffProfile.objects.get_or_create(
                user=user,
                defaults={
                    'name': member_obj.name or getattr(user, 'first_name', '') or f"Member #{member_obj.id}",
                    'name_ta': member_obj.name_ta or '',
                    'phone': safe_phone,
                    'role': validated_data.get('role', 'ACCOUNTANT'),
                    'is_active': True
                }
            )
            if not created:
                profile.is_active = True
                profile.name = member_obj.name or profile.name
                profile.phone = safe_phone
                profile.role = validated_data.get('role', 'ACCOUNTANT')
                profile.save()
            return profile
        else:
            phone = validated_data['phone']
            user = User.objects.create_user(
                username=phone,
                password=validated_data['password'],
                is_staff=False,
            )
            profile = StaffProfile.objects.create(
                user=user,
                name=validated_data['name'],
                name_ta=validated_data.get('name_ta', ''),
                phone=phone,
                role=validated_data.get('role', 'ACCOUNTANT'),
            )
            return profile


# ---------------------------------------------------------------------------
# AccountHead
# ---------------------------------------------------------------------------

class AccountHeadSerializer(serializers.ModelSerializer):
    created_by_name = serializers.SerializerMethodField()
    total_debits = serializers.SerializerMethodField()
    total_credits = serializers.SerializerMethodField()
    net_balance = serializers.SerializerMethodField()

    class Meta:
        model = AccountHead
        fields = [
            'id', 'name', 'name_ta', 'description', 'description_ta', 'head_type',
            'account_type',
            'is_active', 'created_by', 'created_by_name',
            'total_debits', 'total_credits', 'net_balance',
            'created_at', 'updated_at',
        ]
        read_only_fields = ['created_by', 'created_at', 'updated_at', 'total_debits', 'total_credits', 'net_balance']

    def get_created_by_name(self, obj):
        if not obj.created_by:
            return None
        # Try StaffProfile first, then Member, then username
        try:
            return obj.created_by.staff_profile.name
        except Exception:
            pass
        try:
            return obj.created_by.member_profile.name
        except Exception:
            pass
        return obj.created_by.get_username()

    def get_total_debits(self, obj):
        from django.db.models import Sum
        from decimal import Decimal
        debits = obj.transactions.filter(
            is_deleted=False, transaction_type='DEBIT'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
        return str(debits)

    def get_total_credits(self, obj):
        from django.db.models import Sum
        from decimal import Decimal
        credits = obj.transactions.filter(
            is_deleted=False, transaction_type='CREDIT'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0.00')
        return str(credits)

    def get_net_balance(self, obj):
        from decimal import Decimal
        debits = Decimal(self.get_total_debits(obj))
        credits = Decimal(self.get_total_credits(obj))
        
        # Debits - Credits for Assets/Expenses
        if obj.account_type in ['Asset', 'Expense']:
            return str(debits - credits)
        # Credits - Debits for Liabilities/Equity/Revenue
        else:
            return str(credits - debits)

from .models import TrustAccount

class TrustAccountSerializer(serializers.ModelSerializer):
    member_name = serializers.CharField(source='member.name', read_only=True, default='')
    family_member_name = serializers.CharField(source='family_member.name', read_only=True, default='')
    created_by_name = serializers.CharField(source='created_by.username', read_only=True, default='')
    
    class Meta:
        model = TrustAccount
        fields = [
            'account_id', 'account_name', 'account_type', 'associated_entity_type',
            'member', 'member_name', 'family_member', 'family_member_name',
            'account_number', 'bank_name', 'branch_name',
            'status', 'created_date', 'deactivated_date', 'created_by', 'created_by_name'
        ]
        read_only_fields = ['account_id', 'created_date', 'deactivated_date', 'created_by']


# ---------------------------------------------------------------------------
# AccountTransaction
# ---------------------------------------------------------------------------

class AccountTransactionSerializer(serializers.ModelSerializer):
    """Read serializer with nested display fields."""

    account_head_name = serializers.CharField(
        source='account_head.name', read_only=True,
    )
    entered_by_name = serializers.SerializerMethodField()
    member_name = serializers.CharField(
        source='member.name', read_only=True, default=None,
    )
    member_id_display = serializers.CharField(
        source='member.member_id', read_only=True, default=None,
    )
    receipt_number = serializers.CharField(
        source='receipt_number.receipt_number', read_only=True, default=None,
    )
    receipt_id = serializers.IntegerField(
        source='receipt_number.id', read_only=True, default=None,
    )

    tax_event_name = serializers.CharField(
        source='tax_event.name', read_only=True, default=None,
    )
    trust_account_name = serializers.CharField(
        source='trust_account.account_name', read_only=True, default=None,
    )
    tamil_date = serializers.SerializerMethodField()

    class Meta:
        model = AccountTransaction
        fields = [
            'id', 'account_head', 'account_head_name',
            'tax_event', 'tax_event_name',
            'trust_account', 'trust_account_name',
            'transaction_type', 'amount', 'transaction_date', 'tamil_date',
            'payment_mode', 'commodity_type',
            # Income fields
            'donor_name', 'donor_name_ta', 'donor_contact', 'member', 'member_name',
            'member_id_display', 'purpose', 'purpose_ta',
            # Expense fields
            'paid_to', 'paid_to_ta', 'purpose_description', 'purpose_description_ta', 'bill_reference',
            # Tracking
            'proof_document', 'entered_by', 'entered_by_name',
            'is_deleted', 'change_log',
            'receipt_number', 'receipt_id',
            'created_at', 'updated_at',
        ]
        read_only_fields = [
            'entered_by', 'is_deleted', 'change_log',
            'created_at', 'updated_at',
        ]

    def get_tamil_date(self, obj):
        from accounting.utils import get_tamil_date
        return get_tamil_date(obj.transaction_date)

    def get_entered_by_name(self, obj):
        if not obj.entered_by:
            return None
        try:
            return obj.entered_by.staff_profile.name
        except Exception:
            pass
        try:
            return obj.entered_by.member_profile.name
        except Exception:
            pass
        return obj.entered_by.get_username()


class AccountTransactionCreateSerializer(serializers.ModelSerializer):
    """Write serializer with validation."""

    class Meta:
        model = AccountTransaction
        fields = [
            'account_head', 'tax_event', 'trust_account', 'transaction_type', 'amount',
            'transaction_date', 'payment_mode', 'commodity_type',
            'donor_name', 'donor_name_ta', 'donor_contact', 'member', 'purpose', 'purpose_ta',
            'paid_to', 'paid_to_ta', 'purpose_description', 'purpose_description_ta', 'bill_reference',
            'proof_document',
        ]

    def validate_amount(self, value):
        if value <= 0:
            raise serializers.ValidationError(
                "Amount must be greater than zero."
            )
        return value

    def validate_transaction_date(self, value):
        if value > timezone.now().date():
            raise serializers.ValidationError(
                "Transaction date cannot be in the future."
            )
        return value

    def validate_account_head(self, value):
        if not value.is_active:
            raise serializers.ValidationError(
                "Cannot create a transaction under a deactivated Account Head."
            )
        return value

    def validate(self, attrs):
        payment_mode = attrs.get('payment_mode')
        trust_account = attrs.get('trust_account')
        commodity_type = attrs.get('commodity_type')

        if trust_account:
            if payment_mode == 'Cash' and trust_account.account_type != 'Cash':
                raise serializers.ValidationError({"trust_account": "Selected trust account must be of type Cash when payment mode is Cash."})
            elif payment_mode in ['Bank Transfer', 'UPI', 'Cheque'] and trust_account.account_type != 'Bank':
                raise serializers.ValidationError({"trust_account": "Selected trust account must be of type Bank when payment mode is digital/check."})
            elif payment_mode == 'Commodities' and trust_account.account_type != 'Commodities':
                raise serializers.ValidationError({"trust_account": "Selected trust account must be of type Commodities when payment mode is Commodities."})

        if payment_mode == 'Commodities' and not commodity_type:
            raise serializers.ValidationError({"commodity_type": "Commodity Type is required when payment mode is Commodities."})

        # Dynamic validation logic for member vs donor names
        member = attrs.get('member')
        if member:
            attrs['donor_name'] = member.name
            attrs['donor_name_ta'] = member.name_ta or ''
            attrs['donor_contact'] = member.phone or ''
            
            # Enforce custodian member's active trust account on cash/digital/commodity transactions from/to members
            transaction_type = attrs.get('transaction_type')
            if payment_mode != 'Credit':
                target_type = None
                if payment_mode == 'Cash':
                    target_type = 'Cash'
                elif payment_mode in ['Bank Transfer', 'UPI', 'Cheque']:
                    target_type = 'Bank'
                elif payment_mode == 'Commodities':
                    target_type = 'Commodities'

                if target_type:
                    # Resolve the entry member (the logged-in user who is creating the transaction)
                    request = self.context.get('request')
                    user = request.user if request else None
                    entry_member = getattr(user, 'member_profile', None) if user else None

                    # Cash transactions default to the entry member's (logged-in user) Cash trust account.
                    # Other payment modes default to the selected member's (donor) trust account.
                    custodian = entry_member if target_type == 'Cash' else member
                    if not custodian:
                        custodian = member

                    member_account = None
                    if custodian:
                        member_account = TrustAccount.objects.filter(
                            member=custodian,
                            account_type=target_type,
                            status='Active'
                        ).first()

                    if member_account:
                        # For Cash CREDIT transactions, always enforce the custodian account (locked)
                        # For other transactions, default only if not explicitly selected
                        if (transaction_type == 'CREDIT' and target_type == 'Cash') or not attrs.get('trust_account'):
                            attrs['trust_account'] = member_account

                    # Final validation check
                    resolved_account = attrs.get('trust_account')
                    if not resolved_account:
                        if transaction_type == 'CREDIT':
                            raise serializers.ValidationError({
                                "trust_account": f"Please select an active {target_type} Trust Account."
                            })
                    else:
                        if resolved_account.status != 'Active':
                            raise serializers.ValidationError({
                                "trust_account": "The selected Trust Account is inactive."
                            })
                        if resolved_account.account_type != target_type:
                            raise serializers.ValidationError({
                                "trust_account": f"The selected Trust Account type must be {target_type}."
                            })
            
        return attrs


# ---------------------------------------------------------------------------
# Receipt
# ---------------------------------------------------------------------------

from decimal import Decimal

class ReceiptTransactionSerializer(serializers.Serializer):
    account_head = serializers.IntegerField()
    amount = serializers.DecimalField(max_digits=12, decimal_places=2)

class ReceiptSerializer(serializers.ModelSerializer):
    member = serializers.PrimaryKeyRelatedField(queryset=Member.objects.all(), required=False, allow_null=True)
    father_id = serializers.IntegerField(required=False, write_only=True, allow_null=True)
    member_name = serializers.SerializerMethodField()
    member_name_ta = serializers.SerializerMethodField()
    created_by_name = serializers.SerializerMethodField()
    breakup = ReceiptTransactionSerializer(many=True, write_only=True, required=True)
    transactions = serializers.SerializerMethodField()
    tamil_date = serializers.SerializerMethodField()

    class Meta:
        model = Receipt
        fields = [
            'id', 'receipt_number', 'receipt_type', 'member', 'member_name', 'member_name_ta',
            'donor_name', 'father_name', 'father_id',
            'total_amount', 'receipt_date', 'tamil_date', 'payment_mode', 'created_by', 'created_by_name',
            'generated_at', 'pdf_file', 'breakup', 'transactions'
        ]
        read_only_fields = ['receipt_number', 'created_by', 'generated_at', 'pdf_file']

    def get_tamil_date(self, obj):
        from accounting.utils import get_tamil_date
        return get_tamil_date(obj.receipt_date) if obj.receipt_date else ""

    def get_member_name(self, obj):
        if obj.member:
            return obj.member.name
        return obj.donor_name or '—'

    def get_member_name_ta(self, obj):
        if obj.member:
            return obj.member.name_ta
        return obj.donor_name or '—'

    def get_created_by_name(self, obj):
        if not obj.created_by:
            return None
        try:
            return obj.created_by.staff_profile.name
        except Exception:
            pass
        return obj.created_by.username

    def get_transactions(self, obj):
        return [
            {
                'id': t.id,
                'account_head': t.account_head.id,
                'account_head_name': t.account_head.name,
                'account_head_name_ta': t.account_head.name_ta,
                'amount': str(t.amount)
            }
            for t in obj.transactions.filter(is_deleted=False)
        ]

    def validate(self, attrs):
        total_amount = attrs.get('total_amount')
        breakup = attrs.get('breakup', [])
        
        if not breakup:
            raise serializers.ValidationError({
                "breakup": "At least one account head breakdown row is required."
            })
            
        sum_amount = sum(Decimal(str(item['amount'])) for item in breakup)
        if sum_amount != total_amount:
            raise serializers.ValidationError({
                "non_field_errors": "The sum of all account head breakdown items must equal the total amount specified on the receipt."
            })
            
        receipt_type = attrs.get('receipt_type', getattr(self.instance, 'receipt_type', 'Tax'))
        if receipt_type == 'Tax' and not attrs.get('member', getattr(self.instance, 'member', None)):
            raise serializers.ValidationError({
                "member": "A member must be selected for standard tax receipts."
            })
        if receipt_type == 'Seethana':
            has_member = attrs.get('member', getattr(self.instance, 'member', None))
            has_donor = attrs.get('donor_name', getattr(self.instance, 'donor_name', None))
            if not has_member and not has_donor:
                raise serializers.ValidationError({
                    "donor_name": "Sister / Daughter Name is required for Seethana Vari receipts."
                })
                
        return attrs

    def create(self, validated_data):
        breakup_data = validated_data.pop('breakup')
        father_id = validated_data.pop('father_id', None)
        request = self.context.get('request')
        user = request.user if request else None
        
        validated_data['created_by'] = user
        
        receipt_type = validated_data.get('receipt_type', 'Tax')
        member = validated_data.get('member')
        donor_name = validated_data.get('donor_name', '')
        
        if receipt_type == 'Seethana':
            if not member and father_id and donor_name:
                try:
                    father_obj = Member.objects.get(id=father_id)
                    child_obj = Member.objects.create(
                        father=father_obj,
                        name=donor_name,
                        gender='Female',
                        marital_status='Married',
                        address=father_obj.address,
                        address_ta=father_obj.address_ta,
                        is_family_head=False,
                        is_active=True,
                        is_expired=False
                    )
                    validated_data['member'] = child_obj
                    if not validated_data.get('father_name'):
                        validated_data['father_name'] = father_obj.name
                except Member.DoesNotExist:
                    pass
            elif member and not validated_data.get('father_name') and member.father:
                validated_data['father_name'] = member.father.name
            elif member and not validated_data.get('donor_name'):
                validated_data['donor_name'] = member.name
        
        # Create the receipt
        receipt = Receipt.objects.create(**validated_data)
        
        # Save associated breakup transactions
        self._save_transactions(receipt, breakup_data, user)
        
        # Generate the PDF
        self._generate_pdf(receipt)
        
        return receipt

    def update(self, instance, validated_data):
        breakup_data = validated_data.pop('breakup', None)
        validated_data.pop('father_id', None)
        request = self.context.get('request')
        user = request.user if request else None
        
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        if breakup_data is not None:
            # Delete old child transactions
            instance.transactions.all().delete()
            # Create new ones
            self._save_transactions(instance, breakup_data, user)
            
        self._generate_pdf(instance)
        
        return instance

    def _save_transactions(self, receipt, breakup_data, user):
        from .models import TrustAccount
        
        payment_mode = receipt.payment_mode
        target_type = None
        if payment_mode == 'Cash':
            target_type = 'Cash'
        elif payment_mode in ['Bank Transfer', 'UPI', 'Cheque']:
            target_type = 'Bank'
        elif payment_mode == 'Commodities':
            target_type = 'Commodities'
            
        trust_account = None
        if target_type:
            # Optionally attach a trust account if one exists, without blocking receipt creation for regular members
            entry_member = getattr(user, 'member', None) or getattr(user, 'member_profile', None) if user else None
            if entry_member:
                trust_account = TrustAccount.objects.filter(
                    member=entry_member,
                    account_type=target_type,
                    status='Active'
                ).first()
            
            if not trust_account:
                # Fall back to general / Direct Trust accounts of matching payment type
                trust_account = TrustAccount.objects.filter(
                    account_type=target_type,
                    status='Active'
                ).first()
                
        for item in breakup_data:
            AccountTransaction.objects.create(
                receipt_number=receipt,
                account_head_id=item['account_head'],
                amount=item['amount'],
                transaction_type='CREDIT',
                transaction_date=receipt.receipt_date,
                payment_mode=receipt.payment_mode,
                trust_account=trust_account,
                member=receipt.member,
                donor_name=receipt.donor_name or (receipt.member.name if receipt.member else ''),
                entered_by=user,
            )

    def _generate_pdf(self, receipt):
        from .receipt_generator import generate_receipt_pdf
        from django.core.files.base import ContentFile
        import logging
        logger = logging.getLogger(__name__)
        
        if receipt.pdf_file:
            try:
                receipt.pdf_file.delete(save=False)
            except Exception:
                pass
            receipt.pdf_file = None
            
        try:
            pdf_bytes = generate_receipt_pdf(receipt)
            filename = f"{receipt.receipt_number.replace('/', '_')}.pdf"
            receipt.pdf_file.save(filename, ContentFile(pdf_bytes), save=True)
        except Exception as exc:
            logger.error(f"Receipt PDF generation failed for {receipt.receipt_number}: {exc}", exc_info=True)


# ---------------------------------------------------------------------------
# AuditLog
# ---------------------------------------------------------------------------

from .models import AuditLog

class AuditLogSerializer(serializers.ModelSerializer):
    performed_by_name = serializers.CharField(source='performed_by.username', read_only=True)

    class Meta:
        model = AuditLog
        fields = [
            'id', 'action', 'model_name', 'record_id', 'changes', 
            'performed_by', 'performed_by_name', 'timestamp'
        ]
        read_only_fields = fields
