# shop_management/serializers/data/expense.py
from decimal import Decimal
from django.utils import timezone
from rest_framework import serializers
from shop_management.serializers.data.base import DataRootSerializer
from shop_management.models.data.account_transaction import (
    AccountTransaction,
    AccountTransactionType,
)
from shop_management.models.data.expense import Expense, ExpenseCategory
from shop_management.models.data.shop import Shop


class ExpenseCategorySerializer(DataRootSerializer):
    class Meta:
        model = ExpenseCategory  
        fields = '__all__'


class ExpenseSerializer(DataRootSerializer):
    category_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()
    account_details = serializers.SerializerMethodField()
    shop = serializers.PrimaryKeyRelatedField(
        queryset=Shop.objects.all(),
        required=False,
        allow_null=True,
    )
    shop_name = serializers.ReadOnlyField(source="shop.name")
    financial_account = serializers.PrimaryKeyRelatedField(source="account", read_only=True)
    exchange_rate_used = serializers.DecimalField(source="exchange_rate", max_digits=20, decimal_places=6, read_only=True)
    base_amount = serializers.DecimalField(source="converted_amount", max_digits=18, decimal_places=2, read_only=True)
    expense_date = serializers.SerializerMethodField()
    description = serializers.CharField(source="notes", read_only=True)

    class Meta:
        model = Expense
        fields = '__all__'
        read_only_fields = [
            "converted_amount", "expense_number", "status", "posted_at", "posted_by",
            "voided_at", "voided_by", "void_reason",
        ]

    def get_category_details(self, obj):
        if obj.category:
            return {
                "id": obj.category.id,
                "name": obj.category.name,
            }
        return None

    def get_currency_details(self, obj):
        if obj.currency:
            return {
                "id": obj.currency.id,
                "title": obj.currency.title,
                "shop": obj.currency.shop_id,
            }
        return None

    def get_account_details(self, obj):
        if obj.account:
            return {
                "id": obj.account.id,
                "name": obj.account.name,
                "currency_id": obj.account.currency_id,
                "currency_title": obj.account.currency.title if obj.account.currency else None,
            }
        return None

    def get_expense_date(self, obj):
        return obj.payment_date.date() if obj.payment_date else None

    def validate(self, attrs):
        payment_date = attrs.get("payment_date")
        if payment_date is not None and timezone.is_naive(payment_date):
            attrs["payment_date"] = timezone.make_aware(
                payment_date, timezone.get_current_timezone()
            )

        category = attrs.get("category") or getattr(self.instance, "category", None)
        currency = attrs.get("currency") or getattr(self.instance, "currency", None)
        account = attrs.get("account") or getattr(self.instance, "account", None)
        amount = attrs.get("amount") or getattr(self.instance, "amount", None)
        manual_rate = attrs.get("exchange_rate")

        if not currency:
            raise serializers.ValidationError({"currency": "Currency is required."})
        if account is None:
            raise serializers.ValidationError({"account": "Account is required."})
        if not amount or amount <= 0:
            raise serializers.ValidationError({"amount": "Amount must be greater than 0."})
        if account.shop_id != currency.shop_id:
            raise serializers.ValidationError(
                {"account": "Selected account must belong to the same shop as currency."}
            )

        resolved_shop = (
            attrs.get("shop")
            or getattr(self.instance, "shop", None)
            or getattr(currency, "shop", None)
            or getattr(category, "shop", None)
        )
        if resolved_shop and currency.shop_id != resolved_shop.id:
            raise serializers.ValidationError(
                {"currency": "Selected currency must belong to the selected branch."}
            )
        if resolved_shop and account.shop_id != resolved_shop.id:
            raise serializers.ValidationError(
                {"account": "Selected account must belong to the selected branch."}
            )
        if category and getattr(category, "shop_id", None) and resolved_shop and category.shop_id != resolved_shop.id:
            raise serializers.ValidationError(
                {"category": "Selected category must belong to the selected branch."}
            )
        if resolved_shop:
            attrs["shop"] = resolved_shop

        resolved_rate = manual_rate or AccountTransaction.resolve_exchange_rate(
            currency.shop, currency.id, account.currency_id
        )
        if resolved_rate is None:
            raise serializers.ValidationError(
                {"account": "No exchange rate found between expense currency and account currency."}
            )
        if resolved_rate <= 0:
            raise serializers.ValidationError(
                {"exchange_rate": "Exchange rate must be greater than 0."}
            )

        attrs["exchange_rate"] = Decimal(resolved_rate)
        attrs["converted_amount"] = AccountTransaction.compute_converted_amount(
            Decimal(amount), Decimal(resolved_rate)
        )
        return attrs

    def create(self, validated_data):
        expense = super().create(validated_data)
        if not expense.expense_number:
            shop_code = (getattr(expense.shop, "code", "") or f"BR{expense.shop_id}").upper()
            expense.expense_number = f"{shop_code}-EXP-{expense.payment_date.year}-{expense.pk:06d}"
            expense.save(update_fields=["expense_number", "updated_at"])
        return expense

    def update(self, instance, validated_data):
        if instance.status != Expense.Status.DRAFT:
            raise serializers.ValidationError("Only draft expenses can be edited.")
        return super().update(instance, validated_data)
