package com.ga.api.service.statement;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.file.FileService;
import com.ga.common.model.PageResponse;
import com.ga.common.util.SecurityUtil;
import com.ga.core.mapper.org.AgentMapper;
import com.ga.core.mapper.settle.CommissionStatementMapper;
import com.ga.core.mapper.settle.PaymentMapper;
import com.ga.core.vo.org.AgentResp;
import com.ga.core.vo.settle.CommissionStatementResp;
import com.ga.core.vo.settle.CommissionStatementSaveReq;
import com.ga.core.vo.settle.CommissionStatementSearchParam;
import com.ga.core.vo.settle.CommissionStatementVO;
import com.ga.core.vo.settle.PaymentVO;
import com.ga.core.vo.settle.StatementStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class CommissionStatementService {

    private final CommissionStatementMapper statementMapper;
    private final AgentMapper agentMapper;
    private final PaymentMapper paymentMapper;
    private final StatementAggregator aggregator;
    private final ExcelStatementGenerator excelGenerator;
    private final FileService fileService;

    @Transactional(readOnly = true)
    public PageResponse<CommissionStatementResp> list(CommissionStatementSearchParam param) {
        param.startPage();
        return PageResponse.of(statementMapper.selectList(param));
    }

    @Transactional(readOnly = true)
    public CommissionStatementResp detail(Long statementId) {
        CommissionStatementResp resp = statementMapper.selectDetailById(statementId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    @Transactional
    public Long generate(CommissionStatementSaveReq req) {
        if ("PDF".equalsIgnoreCase(req.getFileFormat())) {
            throw new BizException(ErrorCode.INVALID_PARAMETER, "PDF 형식은 아직 지원되지 않습니다");
        }

        AgentResp agent = agentMapper.selectDetailById(req.getAgentId());
        if (agent == null) throw new BizException(ErrorCode.NOT_FOUND, "설계사를 찾을 수 없습니다");

        // 1. 집계
        StatementSummary summary = aggregator.aggregate(req.getAgentId(), req.getSettleMonth());

        // 2. 엑셀 생성
        List<PaymentVO> payments = paymentMapper.selectByAgentAndMonth(req.getAgentId(), req.getSettleMonth());
        byte[] excelBytes = excelGenerator.generate(agent, summary, payments);

        // 3. 파일 저장
        String fileName = "statement_" + req.getAgentId() + "_" + req.getSettleMonth() + ".xlsx";
        Long fileId = fileService.saveBytes(excelBytes, fileName, "COMMISSION_STATEMENT",
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

        // 4. INSERT 또는 UPDATE (재발급)
        CommissionStatementVO existing = statementMapper.selectByAgentMonth(
                req.getAgentId(), req.getSettleMonth(), req.getStatementType());

        Long issuedBy = SecurityUtil.getCurrentUserId();
        LocalDateTime now = LocalDateTime.now();

        if (existing == null) {
            CommissionStatementVO vo = new CommissionStatementVO();
            vo.setAgentId(req.getAgentId());
            vo.setSettleMonth(req.getSettleMonth());
            vo.setStatementType(req.getStatementType());
            vo.setGrossAmount(summary.grossAmount());
            vo.setIncomeTaxAmount(summary.incomeTaxAmount());
            vo.setLocalTaxAmount(summary.localTaxAmount());
            vo.setVatAmount(summary.vatAmount());
            vo.setDeductionAmount(summary.deductionAmount());
            vo.setNetAmount(summary.netAmount());
            vo.setFilePath(String.valueOf(fileId));
            vo.setFileFormat("EXCEL");
            vo.setIssueStatus(StatementStatus.GENERATED.name());
            vo.setIssuedAt(now);
            vo.setIssuedBy(issuedBy != null ? issuedBy.toString() : null);
            statementMapper.insert(vo);
            return vo.getStatementId();
        } else {
            existing.setGrossAmount(summary.grossAmount());
            existing.setIncomeTaxAmount(summary.incomeTaxAmount());
            existing.setLocalTaxAmount(summary.localTaxAmount());
            existing.setVatAmount(summary.vatAmount());
            existing.setDeductionAmount(summary.deductionAmount());
            existing.setNetAmount(summary.netAmount());
            existing.setFilePath(String.valueOf(fileId));
            existing.setFileFormat("EXCEL");
            existing.setIssueStatus(StatementStatus.GENERATED.name());
            existing.setIssuedAt(now);
            existing.setIssuedBy(issuedBy != null ? issuedBy.toString() : null);
            statementMapper.update(existing);
            return existing.getStatementId();
        }
    }

    @Transactional
    public void generateForMonth(String settleMonth, String statementType) {
        List<PaymentVO> allPayments = paymentMapper.selectBySettleMonth(settleMonth);
        List<Long> agentIds = allPayments.stream()
                .map(PaymentVO::getAgentId)
                .distinct()
                .collect(Collectors.toList());

        for (Long agentId : agentIds) {
            CommissionStatementSaveReq req = new CommissionStatementSaveReq();
            req.setAgentId(agentId);
            req.setSettleMonth(settleMonth);
            req.setStatementType(statementType);
            req.setFileFormat("EXCEL");
            generate(req);
        }
    }

    @Transactional
    public byte[] download(Long statementId) {
        CommissionStatementVO vo = statementMapper.selectById(statementId);
        if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);

        Long fileId = Long.parseLong(vo.getFilePath());
        com.ga.common.file.FileVO fileVO = fileService.download(fileId);

        // DOWNLOADED 상태 전이
        statementMapper.updateStatus(statementId, StatementStatus.DOWNLOADED.name());

        try {
            return java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(fileVO.getFilePath()));
        } catch (java.io.IOException e) {
            throw new BizException(ErrorCode.FILE_NOT_FOUND, "파일을 읽을 수 없습니다");
        }
    }

    @Transactional
    public void cancel(Long statementId) {
        CommissionStatementVO vo = statementMapper.selectById(statementId);
        if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
        statementMapper.updateStatus(statementId, StatementStatus.CANCELLED.name());
    }
}
