package com.starry.admin.modules.order.service.impl;

import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
import com.starry.admin.modules.order.module.dto.PaymentInfo;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import java.math.BigDecimal;

abstract class AbstractOrderPlacementStrategy implements OrderPlacementStrategy {

    protected final OrderLifecycleServiceImpl service;

    AbstractOrderPlacementStrategy(OrderLifecycleServiceImpl service) {
        this.service = service;
    }

    @Override
    public OrderPlacementResult place(OrderPlacementCommand command) {
        OrderCreationContext context = command.getOrderContext();
        OrderAmountBreakdown breakdown = handlePricing(command, context);

        PaymentInfo paymentInfo = context.getPaymentInfo();
        if (paymentInfo == null) {
            throw new CustomException("支付信息不能为空");
        }

        BigDecimal netAmount = service.normalizeMoney(paymentInfo.getFinalAmount());
        boolean shouldDeduct = command.isDeductBalance() && service.shouldDeductBalance(context);
        if (shouldDeduct) {
            service.validateSufficientBalance(context.getPurchaserBy(), netAmount);
        }

        PlayOrderInfoEntity order = service.createOrderRecord(context);

        if (shouldDeduct) {
            service.deductCustomerBalance(
                    context.getPurchaserBy(),
                    netAmount,
                    command.getBalanceOperationAction(),
                    context.getOrderId());
        }

        OrderAmountBreakdown amountBreakdown =
                breakdown != null ? breakdown : service.fallbackBreakdown(paymentInfo);
        return OrderPlacementResult.of(order, amountBreakdown);
    }

    protected abstract OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context);
}
