package com.ga.api.service.settle;

import com.ga.common.excel.ExcelErrorRow;
import com.ga.common.excel.ExcelImportResult;
import com.ga.common.excel.ExcelService;
import com.ga.core.mapper.product.ContractMapper;
import com.ga.core.mapper.settle.ChargebackMapper;
import com.ga.core.vo.product.ContractVO;
import com.ga.core.vo.settle.ChargebackUploadExcelVO;
import com.ga.core.vo.settle.ChargebackVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

/**
 * 보험사 환수자료 업로드 서비스.
 *
 * 흐름:
 *  1. ExcelService.importLargeExcel → ChargebackUploadExcelVO 파싱
 *  2. 각 행: policyNo → ContractMapper.selectByPolicyNo 해소
 *     - 미존재 → 에러행 수집(스킵)
 *     - 동일 contractId + settleMonth 이미 존재 → skipCount++ (중복)
 *     - cbAmount null/<=0 → 에러행
 *  3. ChargebackVO 구성 후 insertBatch
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChargebackUploadService {

    private static final DateTimeFormatter YYYYMM = DateTimeFormatter.ofPattern("yyyyMM");

    private final ExcelService excelService;
    private final ContractMapper contractMapper;
    private final ChargebackMapper chargebackMapper;

    @Transactional
    public UploadResult upload(String settleMonth, MultipartFile file) {

        List<ExcelErrorRow> errors  = new ArrayList<>();
        int[] skipCount             = {0};
        int[] successCount          = {0};

        excelService.importLargeExcel(file, ChargebackUploadExcelVO.class, 500, batch -> {
            List<ChargebackVO> toInsert = new ArrayList<>(batch.size());

            for (ChargebackUploadExcelVO row : batch) {
                int rowSeq = successCount[0] + skipCount[0] + errors.size() + 1;

                // 1. cbAmount 검증
                if (row.getCbAmount() == null || row.getCbAmount().compareTo(BigDecimal.ZERO) <= 0) {
                    errors.add(new ExcelErrorRow(rowSeq, "cbAmount",
                            row.getCbAmount() == null ? null : row.getCbAmount().toPlainString(),
                            "환수금액은 0 초과여야 합니다"));
                    continue;
                }

                // 2. 증권번호 → 계약 해소
                if (row.getPolicyNo() == null || row.getPolicyNo().isBlank()) {
                    errors.add(new ExcelErrorRow(rowSeq, "policyNo", null, "증권번호 누락"));
                    continue;
                }
                ContractVO contract = contractMapper.selectByPolicyNo(row.getPolicyNo());
                if (contract == null) {
                    errors.add(new ExcelErrorRow(rowSeq, "policyNo", row.getPolicyNo(),
                            "증권번호에 해당하는 계약을 찾을 수 없습니다"));
                    continue;
                }

                // 3. 중복 확인 (contract_id + settle_month)
                if (chargebackMapper.existsByContractAndMonth(contract.getContractId(), settleMonth) > 0) {
                    skipCount[0]++;
                    log.debug("중복 스킵: contractId={} settleMonth={}", contract.getContractId(), settleMonth);
                    continue;
                }

                // 4. lapseMonth 변환 (YYYYMM → 경과월 INT)
                Integer lapseMonthInt = parseLapseMonth(row.getLapseMonth(), contract);

                // 5. ChargebackVO 구성
                ChargebackVO vo = new ChargebackVO();
                vo.setContractId(contract.getContractId());
                vo.setAgentId(contract.getAgentId());
                vo.setPolicyNo(row.getPolicyNo());
                vo.setLapseMonth(lapseMonthInt);
                vo.setCbRate(row.getCbRate());
                vo.setCbAmount(row.getCbAmount());
                vo.setPaidAmount(BigDecimal.ZERO);
                vo.setRemainAmount(row.getCbAmount());
                vo.setSettleMonth(settleMonth);
                vo.setStatus("NEW");

                toInsert.add(vo);
            }

            if (!toInsert.isEmpty()) {
                chargebackMapper.insertBatch(toInsert);
                successCount[0] += toInsert.size();
            }
        });

        return new UploadResult(successCount[0], errors.size(), skipCount[0], errors);
    }

    /**
     * 실효월 문자열(YYYYMM)을 경과월(INT)로 변환.
     * contract_date 기준 lapseDate까지 개월수를 계산한다.
     * 입력 없음 / 파싱 실패 / contractDate 없음 → null 반환.
     */
    private Integer parseLapseMonth(String lapseMonthStr, ContractVO contract) {
        if (lapseMonthStr == null || lapseMonthStr.isBlank()) return null;
        try {
            YearMonth lapse = YearMonth.parse(lapseMonthStr, YYYYMM);
            if (contract.getContractDate() == null) return null;
            YearMonth contractYm = YearMonth.from(contract.getContractDate());
            long months = ChronoUnit.MONTHS.between(contractYm, lapse);
            return (int) months;
        } catch (Exception e) {
            log.debug("lapseMonth 파싱 실패: value={}", lapseMonthStr);
            return null;
        }
    }

    /** 업로드 결과 DTO. */
    public record UploadResult(
            int successCount,
            int errorCount,
            int skipCount,
            List<ExcelErrorRow> errors
    ) {}
}
