package com.ga.api.service.transfer;

import com.ga.api.transfer.BankTransferFactory;
import com.ga.api.transfer.BankTransferFileGenerator;
import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.file.FileService;
import com.ga.common.util.EncryptUtil;
import com.ga.core.mapper.settle.PaymentMapper;
import com.ga.core.vo.settle.PaymentVO;
import com.ga.core.vo.settle.TransferFileResp;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TransferFileService {

    private final PaymentMapper paymentMapper;
    private final BankTransferFactory factory;
    private final FileService fileService;
    private final EncryptUtil encryptUtil;

    public TransferFileResp generate(String settleMonth, String bankCode) {
        // 1) PENDING payment 조회 — readOnly 트랜잭션 (accountNo는 EncryptTypeHandler로 자동복호화됨)
        List<PaymentVO> payments = paymentMapper.selectPendingByMonthAndBank(settleMonth, bankCode);

        if (payments.isEmpty()) {
            throw new BizException(ErrorCode.NOT_FOUND, "이체 대상 지급 건이 없습니다 (settleMonth=" + settleMonth + ", bankCode=" + bankCode + ")");
        }

        // 2) 파일 byte 생성 + 저장 — 트랜잭션 밖 (IO 작업이므로 커넥션 점유 불필요)
        BankTransferFileGenerator generator = factory.get(bankCode);
        byte[] fileContent = generator.generate(payments, settleMonth);
        String fileName = "transfer_" + settleMonth + "_" + bankCode + generator.getFileExtension();
        Long fileId = fileService.saveBytes(fileContent, fileName, "TRANSFER_FILE", "application/octet-stream");

        // 3) payment 상태 갱신 — 파일 저장 성공 후 별도 트랜잭션 (파일만 잔존하는 역방향 불일치 방지)
        List<Long> paymentIds = payments.stream()
                .map(PaymentVO::getPaymentId)
                .collect(Collectors.toList());
        markPaymentsSent(paymentIds, fileId);

        BigDecimal totalAmount = payments.stream()
                .map(p -> p.getPayAmount() != null ? p.getPayAmount() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        return new TransferFileResp(fileId, fileName, payments.size(), totalAmount);
    }

    @Transactional
    public void markPaymentsSent(List<Long> paymentIds, Long fileId) {
        paymentMapper.updateTransferFile(paymentIds, fileId);
    }
}
