from decimal import Decimal

from django.db import transaction
from rest_framework import serializers

from shop_management.models.data.account_transaction import (
    AccountTransaction,
    AccountTransactionType,
)
from shop_management.models.data.account_transfer import AccountTransfer
from shop_management.serializers.data.base import DataRootSerializer
from account.permissions import assert_shop_access


class AccountTransferSerializer(DataRootSerializer):
    from_account_name = serializers.ReadOnlyField(source="from_account.name")
    to_account_name = serializers.ReadOnlyField(source="to_account.name")
    shop = serializers.ReadOnlyField(source="from_account.shop_id")
    shop_name = serializers.ReadOnlyField(source="from_account.shop.name")
    from_currency_title = serializers.ReadOnlyField(source="from_currency.title")
    to_currency_title = serializers.ReadOnlyField(source="to_currency.title")

    class Meta:
        model = AccountTransfer
        fields = [
            "id",
            "from_account",
            "from_account_name",
            "to_account",
            "to_account_name",
            "shop",
            "shop_name",
            "from_currency",
            "from_currency_title",
            "to_currency",
            "to_currency_title",
            "amount",
            "exchange_rate",
            "converted_amount",
            "service_charge",
            "service_charge_account",
            "transfer_number",
            "uses_controlled_engine",
            "status",
            "posted_at",
            "posted_by",
            "voided_at",
            "voided_by",
            "void_reason",
            "reference",
            "note",
            "transfer_date",
            "created_at",
            "updated_at",
        ]
        read_only_fields = [
            "from_currency",
            "to_currency",
            "converted_amount",
            "created_at",
            "updated_at",
            "transfer_number",
            "status",
            "posted_at",
            "posted_by",
            "voided_at",
            "voided_by",
            "void_reason",
        ]
        extra_kwargs = {"exchange_rate": {"required": False}}

    def validate(self, attrs):
        from_account = attrs.get("from_account") or getattr(
            self.instance, "from_account", None
        )
        to_account = attrs.get("to_account") or getattr(self.instance, "to_account", None)
        amount = attrs.get("amount") or getattr(self.instance, "amount", None)

        if not from_account:
            raise serializers.ValidationError({"from_account": "From account is required."})
        if not to_account:
            raise serializers.ValidationError({"to_account": "To account is required."})
        if from_account.id == to_account.id:
            raise serializers.ValidationError(
                {"to_account": "From account and to account cannot be the same."}
            )
        if from_account.shop_id != to_account.shop_id:
            raise serializers.ValidationError(
                {"to_account": "Both accounts must belong to the same shop."}
            )
        request = self.context.get("request")
        if request:
            assert_shop_access(request.user, from_account.shop, request)
        if not amount or amount <= 0:
            raise serializers.ValidationError({"amount": "Amount must be greater than 0."})

        resolved_rate = AccountTransaction.resolve_exchange_rate(
            from_account.shop, from_account.currency_id, to_account.currency_id,
            attrs.get("transfer_date") or getattr(self.instance, "transfer_date", None),
        )
        provided_rate = attrs.get("exchange_rate")
        if provided_rate is not None:
            if provided_rate <= 0:
                raise serializers.ValidationError(
                    {"exchange_rate": "Exchange rate must be greater than 0."}
                )
            final_rate = Decimal(provided_rate)
        elif resolved_rate is None:
            raise serializers.ValidationError(
                {
                    "to_account": (
                        "No exchange rate found between source and destination "
                        "account currencies for this shop."
                    )
                }
            )
        else:
            final_rate = Decimal(resolved_rate)

        attrs["from_currency"] = from_account.currency
        attrs["to_currency"] = to_account.currency
        attrs["exchange_rate"] = final_rate
        attrs["converted_amount"] = AccountTransaction.compute_converted_amount(
            Decimal(amount), final_rate
        )
        return attrs

    def _txn_description(self, transfer, direction):
        prefix = "Transfer out" if direction == "out" else "Transfer in"
        target = (
            f"to {transfer.to_account.name}"
            if direction == "out"
            else f"from {transfer.from_account.name}"
        )
        reference = f" | Ref: {transfer.reference}" if transfer.reference else ""
        note = f" | Note: {transfer.note}" if transfer.note else ""
        return f"{prefix} {target}{reference}{note}"

    def _delete_account_transactions(self, transfer):
        AccountTransaction.objects.filter(
            account=transfer.from_account,
            transaction_type=AccountTransactionType.DEBIT,
            amount=transfer.amount,
            transaction_date=transfer.transfer_date,
            description=self._txn_description(transfer, "out"),
        ).delete()
        AccountTransaction.objects.filter(
            account=transfer.to_account,
            transaction_type=AccountTransactionType.CREDIT,
            amount=transfer.amount,
            transaction_date=transfer.transfer_date,
            description=self._txn_description(transfer, "in"),
        ).delete()

    def create(self, validated_data):
        with transaction.atomic():
            if validated_data.get("uses_controlled_engine"):
                validated_data["status"] = AccountTransfer.Status.DRAFT
                return super().create(validated_data)
            transfer = super().create(validated_data)

            # Double-entry posting:
            # 1) Debit source account in source currency.
            AccountTransaction.objects.create(
                account=transfer.from_account,
                account_currency=transfer.from_currency,
                transaction_currency=transfer.from_currency,
                transaction_type=AccountTransactionType.DEBIT,
                amount=transfer.amount,
                exchange_rate=Decimal("1"),
                converted_amount=transfer.amount,
                description=self._txn_description(transfer, "out"),
                transaction_date=transfer.transfer_date,
            )

            # 2) Credit destination account using source currency + exchange rate.
            AccountTransaction.objects.create(
                account=transfer.to_account,
                account_currency=transfer.to_currency,
                transaction_currency=transfer.from_currency,
                transaction_type=AccountTransactionType.CREDIT,
                amount=transfer.amount,
                exchange_rate=transfer.exchange_rate,
                converted_amount=transfer.converted_amount,
                description=self._txn_description(transfer, "in"),
                transaction_date=transfer.transfer_date,
            )

            return transfer

    def update(self, instance, validated_data):
        with transaction.atomic():
            if instance.uses_controlled_engine:
                if instance.status != AccountTransfer.Status.DRAFT:
                    raise serializers.ValidationError("Only draft controlled transfers can be edited.")
                return super().update(instance, validated_data)
            self._delete_account_transactions(instance)
            transfer = super().update(instance, validated_data)

            AccountTransaction.objects.create(
                account=transfer.from_account,
                account_currency=transfer.from_currency,
                transaction_currency=transfer.from_currency,
                transaction_type=AccountTransactionType.DEBIT,
                amount=transfer.amount,
                exchange_rate=Decimal("1"),
                converted_amount=transfer.amount,
                description=self._txn_description(transfer, "out"),
                transaction_date=transfer.transfer_date,
            )

            AccountTransaction.objects.create(
                account=transfer.to_account,
                account_currency=transfer.to_currency,
                transaction_currency=transfer.from_currency,
                transaction_type=AccountTransactionType.CREDIT,
                amount=transfer.amount,
                exchange_rate=transfer.exchange_rate,
                converted_amount=transfer.converted_amount,
                description=self._txn_description(transfer, "in"),
                transaction_date=transfer.transfer_date,
            )

            return transfer

