"""Financial settlement boundary for player transfers.

Transfer owns the business meaning of a transfer fee.  Economy owns token
movement, wallet locking and immutable ledger history.  This adapter never
mutates a Wallet directly.
"""

from decimal import Decimal

from economy.constants import LedgerTransactionType
from economy.exceptions import (
    IdempotencyConflictError,
    InsufficientBalanceError,
    TokenBalanceOverflowError,
    WalletInactiveError,
    WalletNotFoundError,
)
from economy.services import EconomyTransferService, WalletService
from club_finance.constants import FinanceCategory
from club_finance.services import BudgetService, LedgerClassificationService

from .exceptions import (
    InvalidTransferFeeError,
    TransferCompletionFinancialError,
    TransferInsufficientFundsError,
    TransferWalletUnavailableError,
)


class TransferFinancialService:
    @staticmethod
    def resolve_settlement_wallets(*, transfer, signing_bonus):
        """Resolve all wallets a completion may touch without locking them.

        The caller can then lock the union canonically before the first
        financial leg.  This is important because row locks survive until the
        outer transaction commits.
        """
        wallets = []

        if transfer.transfer_fee > Decimal("0"):
            if transfer.source_club_id is None:
                raise InvalidTransferFeeError(
                    "A free-agent transfer cannot have a transfer fee."
                )
            wallets.append(
                WalletService.get_or_create_club_wallet(
                    club=transfer.destination_club,
                )
            )
            wallets.append(
                WalletService.get_or_create_club_wallet(
                    club=transfer.source_club,
                )
            )

        if signing_bonus > Decimal("0"):
            wallets.append(
                WalletService.get_or_create_club_wallet(
                    club=transfer.destination_club,
                )
            )
            wallets.append(
                WalletService.get_or_create_player_wallet(
                    player=transfer.player,
                )
            )

        return wallets

    @staticmethod
    def settle_transfer_fee(*, transfer):
        amount = transfer.transfer_fee
        if amount == Decimal("0"):
            return None

        if transfer.source_club_id is None:
            raise InvalidTransferFeeError(
                "A free-agent transfer cannot have a transfer fee."
            )

        try:
            from club_debt.services import InsolvencyRestrictionService
            InsolvencyRestrictionService.assert_allowed(transfer.destination_club, "transfer_spend")
            budget_check = BudgetService.check_spend(
                club=transfer.destination_club,
                category=FinanceCategory.TRANSFER_SPEND,
                amount=amount,
            )
            if not budget_check["allowed"]:
                raise TransferCompletionFinancialError(
                    "Transfer fee exceeds the club hard budget limit."
                )
            destination_wallet = WalletService.get_or_create_club_wallet(
                club=transfer.destination_club,
            )
            source_wallet = WalletService.get_or_create_club_wallet(
                club=transfer.source_club,
            )
            ledger = EconomyTransferService.transfer(
                source_wallet=destination_wallet,
                destination_wallet=source_wallet,
                amount=amount,
                transaction_type=LedgerTransactionType.TRANSFER_FEE,
                reference_type="transfer",
                reference_id=str(transfer.pk),
                idempotency_key=f"transfer-fee:{transfer.pk}",
                description="Player transfer fee",
            )
            LedgerClassificationService.classify_entry(club=transfer.destination_club, entry=ledger)
            LedgerClassificationService.classify_entry(club=transfer.source_club, entry=ledger)
            return ledger
        except InsufficientBalanceError as exc:
            raise TransferInsufficientFundsError(
                "Destination club does not have enough available balance for "
                "the transfer fee."
            ) from exc
        except (WalletInactiveError, WalletNotFoundError) as exc:
            raise TransferWalletUnavailableError(
                "Transfer fee settlement requires active source and destination "
                "club wallets."
            ) from exc
        except (IdempotencyConflictError, TokenBalanceOverflowError) as exc:
            raise TransferCompletionFinancialError(
                "Transfer fee could not be settled safely."
            ) from exc
