package cn.novalon.gym.manage.member.handler;

import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.entity.MemberCardTransaction;
import cn.novalon.gym.manage.member.enums.CardEvent;
import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
import cn.novalon.gym.manage.member.enums.TransactionType;
import cn.novalon.gym.manage.member.repository.MemberCardRecordRepository;
import cn.novalon.gym.manage.member.service.IMemberCardTransactionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * 退款 Saga 处理器
 * 
 * @author 付嘉
 * @date 2026-05-27
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RefundSagaHandler {

    private final MemberCardRecordRepository recordRepository;
    private final IMemberCardTransactionService transactionService;
    private final MemberCardStateMachine stateMachine;

    public Mono<Void> executeRefund(Long recordId) {
        return recordRepository.findById(recordId)
                .switchIfEmpty(Mono.error(new RuntimeException("会员卡记录不存在")))
                .flatMap(record -> stateMachine.validateTransition(record, CardEvent.REFUND)
                        .then(Mono.defer(() -> doExecuteRefund(recordId, record))));
    }

    private Mono<Void> doExecuteRefund(Long recordId, MemberCardRecord record) {
        List<SagaStep> steps = new ArrayList<>();
        List<SagaStep> rollbackSteps = new ArrayList<>();

        SagaStep step1 = new SagaStep(
                "更新会员卡状态为已退款",
                updateCardStatus(recordId, MemberCardRecordStatus.REFUNDED),
                Mono.defer(() -> updateCardStatus(recordId, record.getStatus()))
        );
        steps.add(step1);
        rollbackSteps.add(0, step1);

        SagaStep step2 = new SagaStep(
                "记录退款流水",
                createRefundTransaction(record),
                createReversalTransaction(record)
        );
        steps.add(step2);
        rollbackSteps.add(0, step2);

        return executeSaga(steps, rollbackSteps);
    }

    private Mono<Void> updateCardStatus(Long recordId, MemberCardRecordStatus status) {
        return recordRepository.updateStatus(recordId, status.name())
                .flatMap(rows -> {
                    if (rows == 0) {
                        return Mono.error(new RuntimeException("更新会员卡状态失败"));
                    }
                    return Mono.empty();
                });
    }

    private Mono<Void> createRefundTransaction(MemberCardRecord record) {
        MemberCardTransaction transaction = MemberCardTransaction.builder()
                .memberId(record.getMemberId())
                .memberCardId(record.getMemberCardId())
                .operationType(TransactionType.REFUND.name())
                .changeAmount(-record.getRemainingTimes())
                .changeBalance(-record.getRemainingAmount())
                .afterRemainingCount(0)
                .afterRemainingBalance(0.0)
                .remark("会员卡退款")
                .build();

        return transactionService.createTransaction(transaction);
    }

    private Mono<Void> createReversalTransaction(MemberCardRecord record) {
        MemberCardTransaction reversal = MemberCardTransaction.builder()
                .memberId(record.getMemberId())
                .memberCardId(record.getMemberCardId())
                .operationType(TransactionType.REFUND.name())
                .changeAmount(record.getRemainingTimes())
                .changeBalance(record.getRemainingAmount())
                .remark("退款冲正")
                .build();

        return transactionService.createTransaction(reversal);
    }

    private Mono<Void> executeSaga(List<SagaStep> steps, List<SagaStep> rollbackSteps) {
        return executeStep(steps, 0, rollbackSteps);
    }

    private Mono<Void> executeStep(List<SagaStep> steps, int index, List<SagaStep> rollbackSteps) {
        if (index >= steps.size()) {
            return Mono.empty();
        }

        SagaStep currentStep = steps.get(index);

        return currentStep.operation()
                .then(Mono.defer(() -> executeStep(steps, index + 1, rollbackSteps)))
                .onErrorResume(error -> {
                    log.error("Saga步骤执行失败: step={}, error={}", currentStep.description(), error.getMessage());
                    return rollback(rollbackSteps, 0).then(Mono.error(error));
                });
    }

    private Mono<Void> rollback(List<SagaStep> rollbackSteps, int index) {
        if (index >= rollbackSteps.size()) {
            return Mono.empty();
        }

        SagaStep currentStep = rollbackSteps.get(index);

        return currentStep.rollbackOperation()
                .then(Mono.defer(() -> rollback(rollbackSteps, index + 1)))
                .doOnError(error -> log.error("Saga回滚失败: step={}, error={}", currentStep.description(), error.getMessage()))
                .onErrorResume(e -> Mono.empty());
    }

    private record SagaStep(String description, Mono<Void> operation, Mono<Void> rollbackOperation) {}
}
