package com.banesco.module.service_issue_payment_status.infrastructure.mapper;

import com.banesco.common.domain.model.WorkProduct;
import com.banesco.module.payment_status.domain.dto.request.PaymentStatusRequest;
import com.banesco.module.payment_status.domain.model.PaymentStatus;
import com.banesco.module.service_issue_payment_status.domain.dto.request.ServiceIssuePaymentStatusRequest;
import com.banesco.module.service_issue_payment_status.domain.model.ServicingIssueProcedure;
import com.banesco.module.instruction.domain.model.*;
import com.banesco.common.domain.model.Identifier;
import com.banesco.module.banking_product.domain.model.*;
import com.banesco.module.party.domain.model.*;
import com.banesco.module.transaction.domain.model.*;
import java.math.BigDecimal;
import java.util.List;

public class ServicingIssueMapper {

    private ServicingIssueMapper() {}

    public static PaymentStatusRequest toPaymentRequest(
        ServiceIssuePaymentStatusRequest request
    ) {
        return PaymentStatusRequest.builder()
            .fintechId(request.getCustomerReferenceFintechId())
            .id(Long.valueOf(request.getId()))
            .channelOrigin(request.getChannelCode())
            .build();
    }

    public static ServicingIssueProcedure toModel(PaymentStatus response) {
        return ServicingIssueProcedure.builder()
            .instructionReference(buildInstruction(response))
            .procedureType(response.getTypeOperation())
            .bankingProduct(buildBankingProduct(response))
            .customerReference(buildParty(response.getIdentApplicant()))
            .associatedPartyReference(buildParty(response.getIdentRecipient()))
            .transaction(buildTransaction(response))
            .workProduct(buildWorkProduct(response))
            .build();
    }

    private static Instruction buildInstruction(PaymentStatus response) {
        return Instruction.builder()
            .instructionIdentifier(InstructionIdentification.builder()
                .identification(Identifier.builder()
                    .identifierValue(response.getId().toString())
                    .build())
                .identificationType(mapInstructionIdentificationType())
                .build())
            .build();
    }

    private static BankingProduct buildBankingProduct(PaymentStatus response) {
        return BankingProduct.builder()
            .productType(mapBankingProductType(response.getCode()))
            .productDescription(response.getName())
            .build();
    }

    private static Party buildParty(String partyIdentifier) {
        return Party.builder()
            .partyIdentification(List.of(PartyIdentification.builder()
                .partyIdentification(Identifier.builder()
                    .identifierValue(partyIdentifier)
                    .build())
                .build()))
            .build();
    }

    private static Transaction buildTransaction(PaymentStatus response) {
        return Transaction.builder()
            .financialTransaction(FinancialTransaction.builder()
                .amount(new BigDecimal(response.getAmount()))
                .currencyCode(response.getCurrency())
                .build())
            .transactionStatus(buildTransactionStatus(response))
            .transactionDate(buildTransactionDateTimeList(response))
            .transactionTrace(TransactionTrace.builder()
                .traceChannel(response.getTraceChannel())
                .traceReference(response.getTraceRef())
                .build())
            .transactionAuthorization(buildTransactionAuthorization(response))
            .transactionDetails(TransactionDetails.builder()
                .transactionDataJson(response.getPaymentDataJson())
                .build())
            .description(response.getConcept())
            .build();
    }

    private static TransactionStatus buildTransactionStatus(PaymentStatus response) {
        return TransactionStatus.builder()
            .identification(Identifier.builder()
                .identifierValue(response.getIdStatusRequest())
                .build())
            .statusType(mapTransactionStatusType(response.getCodeStatusRequest()))
            .statusName(response.getNameStatusRequest())
            .statusRejectedCause(response.getRejectionReason())
            .build();
    }

    private static List<TransactionDateTime> buildTransactionDateTimeList(PaymentStatus response) {
        return List.of(
            TransactionDateTime.builder()
                .dateType(TransactionDateTimeType.INITIATED_DATE)
                .date(response.getDateCreate())
                .build(),
            TransactionDateTime.builder()
                .dateType(TransactionDateTimeType.EXECUTED_DATE)
                .date(response.getDateModify())
                .build(),
            TransactionDateTime.builder()
                .dateType(TransactionDateTimeType.AVAILABLE_DATE)
                .date(response.getAvailabilityDate())
                .build(),
            TransactionDateTime.builder()
                .dateType(TransactionDateTimeType.EXPIRY_DATE)
                .date(response.getExpirationDate())
                .build(),
            TransactionDateTime.builder()
                .dateType(TransactionDateTimeType.EXPIRY_TIME)
                .time(response.getExpirationTime())
                .build()
        );
    }

    private static TransactionAuthorization buildTransactionAuthorization(PaymentStatus response) {
        return TransactionAuthorization.builder()
            .identification(Identifier.builder()
                .identifierValue(response.getUuidPasskey())
                .build())
            .authorizationType(response.getAuthorizationType())
            .build();
    }

    private static WorkProduct buildWorkProduct(PaymentStatus response) {
        return WorkProduct.builder()
            .productIdentification(Identifier.builder()
                .identifierValue(response.getIdDifussion())
                .build())
            .productReference(response.getReqQuery())
            .build();
    }

    private static InstructionIdentificationType mapInstructionIdentificationType() {
        return InstructionIdentificationType.INSTRUCTION_NUMBER;
    }

    private static BankingProductType mapBankingProductType(String code) {
        try {
            return BankingProductType.valueOf(code);
        } catch (IllegalArgumentException | NullPointerException e) {
            return BankingProductType.MOBILE_PAYMENT;
        }
    }

    private static TransactionStatusType mapTransactionStatusType(String codeStatusRequest) {
        if (codeStatusRequest == null) {
            return TransactionStatusType.PENDING;
        }

        return switch (codeStatusRequest.toUpperCase()) {
            case "ACT", "ACTIVO", "ACTIVA" -> TransactionStatusType.ACTIVE;
            case "PENDIENTE" -> TransactionStatusType.PENDING;
            case "EXITO", "EXITOSA", "COMPLETADO", "FINALIZADO" -> TransactionStatusType.COMPLETED;
            case "APROBADO", "APROBADA" -> TransactionStatusType.APPROVED;
            case "CONF", "CONFIRMADO", "CONFIRMADA" -> TransactionStatusType.CONFIRMED;
            case "INICIADO" -> TransactionStatusType.INITIATED;
            case "EN_PROCESO", "PROCESANDO", "EN CURSO" -> TransactionStatusType.IN_PROGRESS;
            case "RECH", "RECHAZADO", "FALLA", "FALLIDO", "RECHAZADA", "FALLIDA" -> TransactionStatusType.REJECTED;
            case "CANCELADO" -> TransactionStatusType.CANCELLED;
            case "SUSPENDIDO" -> TransactionStatusType.SUSPENDED;
            case "NOTIFICADO" -> TransactionStatusType.NOTIFIED;
            case "CONTABILIZADO" -> TransactionStatusType.BOOKED;
            case "EJECUTADO" -> TransactionStatusType.EXECUTED;
            case "EXP", "EXPIRADO", "EXPIRADA" -> TransactionStatusType.EXPIRED;
            case "ENVIADO", "ENVIADA" -> TransactionStatusType.SENT;
            case "RECIBIDO", "RECIBIDA" -> TransactionStatusType.RECEIVED;
            case "LEIDA" -> TransactionStatusType.READ;
            case "PROG", "PROGRAMADO", "PROGRAMADA" -> TransactionStatusType.PROGRAMED;
            case "TRANSATIP" -> TransactionStatusType.ATYPICAL_TRANSACTION;
            default -> TransactionStatusType.DEFAULT;
        };
    }
}