import 'package:pshared/data/dto/payment/card.dart';
import 'package:pshared/data/dto/payment/card_token.dart';
import 'package:pshared/data/dto/payment/endpoint.dart';
import 'package:pshared/data/dto/payment/external_chain.dart';
import 'package:pshared/data/dto/payment/iban.dart';
import 'package:pshared/data/dto/payment/ledger.dart';
import 'package:pshared/data/dto/payment/managed_wallet.dart';
import 'package:pshared/data/dto/payment/russian_bank.dart';
import 'package:pshared/data/dto/payment/wallet.dart';
import 'package:pshared/data/mapper/payment/asset.dart';
import 'package:pshared/data/mapper/payment/enums.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/card_token.dart';
import 'package:pshared/models/payment/methods/crypto_address.dart';
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/methods/iban.dart';
import 'package:pshared/models/payment/methods/ledger.dart';
import 'package:pshared/models/payment/methods/managed_wallet.dart';
import 'package:pshared/models/payment/methods/russian_bank.dart';
import 'package:pshared/models/payment/methods/wallet.dart';
import 'package:pshared/models/payment/type.dart';

extension PaymentMethodDataEndpointMapper on PaymentMethodData {
  PaymentEndpointDTO toDTO() {
    final metadata = this.metadata;

    switch (type) {
      case PaymentType.ledger:
        final payload = this as LedgerPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: LedgerEndpointDTO(
            ledgerAccountRef: payload.ledgerAccountRef,
            contraLedgerAccountRef: payload.contraLedgerAccountRef,
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.managedWallet:
        final payload = this as ManagedWalletPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: ManagedWalletEndpointDTO(
            managedWalletRef: payload.managedWalletRef,
            asset: payload.asset?.toDTO(),
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.externalChain:
        final payload = this as CryptoAddressPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: ExternalChainEndpointDTO(
            asset: payload.asset?.toDTO(),
            address: payload.address,
            memo: payload.memo,
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.card:
        final payload = this as CardPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: CardEndpointDTO(
            pan: payload.pan,
            expMonth: payload.expMonth,
            expYear: payload.expYear,
            country: payload.country,
            firstName: payload.firstName,
            lastName: payload.lastName,
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.cardToken:
        final payload = this as CardTokenPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: CardTokenEndpointDTO(
            token: payload.token,
            maskedPan: payload.maskedPan,
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.wallet:
        final payload = this as WalletPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: WalletPaymentDataDTO(walletId: payload.walletId).toJson(),
          metadata: metadata,
        );
      case PaymentType.bankAccount:
        final payload = this as RussianBankAccountPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: RussianBankAccountPaymentDataDTO(
            recipientName: payload.recipientName,
            inn: payload.inn,
            kpp: payload.kpp,
            bankName: payload.bankName,
            bik: payload.bik,
            accountNumber: payload.accountNumber,
            correspondentAccount: payload.correspondentAccount,
          ).toJson(),
          metadata: metadata,
        );
      case PaymentType.iban:
        final payload = this as IbanPaymentMethod;
        return PaymentEndpointDTO(
          type: endpointTypeToValue(type),
          data: IbanPaymentDataDTO(
            iban: payload.iban,
            accountHolder: payload.accountHolder,
            bic: payload.bic,
            bankName: payload.bankName,
          ).toJson(),
          metadata: metadata,
        );
    }
  }
}

extension PaymentEndpointDTOMapper on PaymentEndpointDTO {
  PaymentMethodData toDomain() {
    final metadata = this.metadata;

    switch (_resolveEndpointType(type, data)) {
      case PaymentType.ledger:
        final payload = LedgerEndpointDTO.fromJson(data);
        return LedgerPaymentMethod(
          ledgerAccountRef: payload.ledgerAccountRef,
          contraLedgerAccountRef: payload.contraLedgerAccountRef,
          metadata: metadata,
        );
      case PaymentType.managedWallet:
        final payload = ManagedWalletEndpointDTO.fromJson(data);
        return ManagedWalletPaymentMethod(
          managedWalletRef: payload.managedWalletRef,
          asset: payload.asset?.toDomain(),
          metadata: metadata,
        );
      case PaymentType.externalChain:
        final payload = ExternalChainEndpointDTO.fromJson(data);
        return CryptoAddressPaymentMethod(
          asset: payload.asset?.toDomain(),
          address: payload.address,
          memo: payload.memo,
          metadata: metadata,
        );
      case PaymentType.card:
        final payload = CardEndpointDTO.fromJson(data);
        return CardPaymentMethod(
          pan: payload.pan,
          firstName: payload.firstName,
          lastName: payload.lastName,
          expMonth: payload.expMonth,
          expYear: payload.expYear,
          country: payload.country,
          metadata: metadata,
        );
      case PaymentType.cardToken:
        final payload = CardTokenEndpointDTO.fromJson(data);
        return CardTokenPaymentMethod(
          token: payload.token,
          maskedPan: payload.maskedPan,
          metadata: metadata,
        );
      case PaymentType.wallet:
        final payload = WalletPaymentDataDTO.fromJson(data);
        return WalletPaymentMethod(
          walletId: payload.walletId,
          metadata: metadata,
        );
      case PaymentType.bankAccount:
        final payload = RussianBankAccountPaymentDataDTO.fromJson(data);
        return RussianBankAccountPaymentMethod(
          recipientName: payload.recipientName,
          inn: payload.inn,
          kpp: payload.kpp,
          bankName: payload.bankName,
          bik: payload.bik,
          accountNumber: payload.accountNumber,
          correspondentAccount: payload.correspondentAccount,
          metadata: metadata,
        );
      case PaymentType.iban:
        final payload = IbanPaymentDataDTO.fromJson(data);
        return IbanPaymentMethod(
          iban: payload.iban,
          accountHolder: payload.accountHolder,
          bic: payload.bic,
          bankName: payload.bankName,
          metadata: metadata,
        );
    }
  }
}

PaymentType _resolveEndpointType(String type, Map<String, dynamic> data) {
  if (type == 'card' &&
      (data.containsKey('token') || data.containsKey('masked_pan'))) {
    return PaymentType.cardToken;
  }
  return endpointTypeFromValue(type);
}
