package com.yd.csf.api.controller;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.csf.api.dto.CommissionExcelDTO;
import com.yd.csf.service.common.ErrorCode;
import com.yd.csf.service.component.CommissionAsyncService;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.CommissionStatusEnum;
import com.yd.csf.service.model.Commission;
import com.yd.csf.service.model.CommissionCompareRecord;
import com.yd.csf.service.model.CommissionEditRecord;
import com.yd.csf.service.model.PolicyFollow;
import com.yd.csf.service.service.*;
import com.yd.csf.service.vo.CommissionStatisticsVO;
import com.yd.csf.service.vo.CommissionVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 保单来佣接口
 *
 * @author jianan
 * @since 2025-09-15
 */
@RestController
@RequestMapping("/commission")
@Tag(name = "保单来佣接口")
public class ApiCommissionController {

    private static final Logger log = LoggerFactory.getLogger(ApiCommissionController.class);

    @Resource
    private CommissionService commissionService;
    @Resource
    private CommissionExpectedService commissionExpectedService;
    @Resource
    private CommissionEditRecordService commissionEditRecordService;
    @Resource
    private CommissionCompareRecordService commissionCompareRecordService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CommissionAsyncService commissionAsyncService;
    @Resource
    private PolicyFollowService policyFollowService;


    @PostMapping("/upload/excel")
    @Operation(summary = "上传入账Excel文件")
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> uploadExcel(@RequestParam("file") MultipartFile file,
                                       @RequestParam("reconciliationYearMonth") String reconciliationYearMonth) throws IOException {

        // 校验来佣检核年月是否为空
        if (Objects.isNull(reconciliationYearMonth)) {
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "来佣检核年月不能为空");
        }

        List<CommissionExcelDTO> dataList = new ArrayList<>();
        try {
            dataList = EasyExcel.read(file.getInputStream())
                    .head(CommissionExcelDTO.class)
                    .sheet("来佣")
                    .doReadSync();

        } catch (IOException e) {
            throw new RuntimeException("Excel读取失败", e);
        }
        // 根据来佣检核年月查询是否已存在来佣记录
        List<Commission> existingCommissions = commissionService.list(new QueryWrapper<Commission>()
                .eq("reconciliation_year_month", reconciliationYearMonth));
        if (CollectionUtils.isNotEmpty(existingCommissions)) {
            boolean isAllCompareSuccess = existingCommissions.stream()
                    .allMatch(commission -> commission.getStatus().equals(CommissionStatusEnum.COMPARE_SUCCESS.getItemValue()));
            if (isAllCompareSuccess) {
                // 所有来佣记录都已检核成功，不允许上传新记录
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "当前来佣检核年月所有记录都已检核成功，不允许上传新记录，若需更新请手动操作");
            }
        }

        // 数据完整性校验
        validateCommissionUpload(dataList);

        // 校验保单是否存在
        Set<String> policyNos = dataList.stream()
                .map(CommissionExcelDTO::getPolicyNo)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());
        List<PolicyFollow> policyFollows = policyFollowService.list(new LambdaQueryWrapper<PolicyFollow>()
                .in(PolicyFollow::getPolicyNo, policyNos)
                .select(PolicyFollow::getPolicyNo));
        Set<String> existingPolicyNos = policyFollows.stream().map(PolicyFollow::getPolicyNo).collect(Collectors.toSet());

        StringBuilder errorMsg = new StringBuilder();
        for (CommissionExcelDTO excelDTO : dataList) {
            if (StringUtils.isNotBlank(excelDTO.getPolicyNo())) {
                if (!existingPolicyNos.contains(excelDTO.getPolicyNo())) {
                    errorMsg.append("第").append(dataList.indexOf(excelDTO) + 1).append("行保单不存在");
                }
            }
        }
        if (StringUtils.isNotBlank(errorMsg)) {
            errorMsg.append("\n请确认新单跟进中保单是否存在，若不存在请先添加保单");
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), errorMsg.toString());
        }

        // 获取当前登录用户的ID
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 先删除已存在的来佣记录
        commissionService.remove(new LambdaQueryWrapper<Commission>()
                .eq(Commission::getReconciliationYearMonth, reconciliationYearMonth));

        // 数据处理
        List<Commission> entities = processData(dataList, loginUserId);

        // 批量比对
        TransactionSynchronizationManager.registerSynchronization(
                new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {
                        try {
                            // 重新查询最新的数据，获取已提交的数据
                            commissionAsyncService.commissionCompareBatch(entities);
                        } catch (Exception e) {
                            // 比对失败不影响主事务，记录日志即可
                            log.error("批量导入, 比对操作执行失败, error: {}", e.getMessage());
                        }
                    }
                }
        );

        return Result.success(true);
    }

    /**
     * 校验导入的来佣数据是否符合要求，返回行号和错误信息
     */
    private void validateCommissionUpload(List<CommissionExcelDTO> dataList) {
        // 校验数据是否为空
        if (CollectionUtils.isEmpty(dataList)) {
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "导入数据不能为空");
        }
        StringBuilder errorMsg = new StringBuilder();
        for (CommissionExcelDTO excelDTO : dataList) {
            // 校验必填字段是否为空
            if (StringUtils.isBlank(excelDTO.getPolicyNo()) ||
                    StringUtils.isBlank(excelDTO.getReconciliationCompany()) ||
                    StringUtils.isBlank(excelDTO.getCommissionName()) ||
                    excelDTO.getAmount() == null ||
                    StringUtils.isBlank(excelDTO.getCurrency()) ||
                    excelDTO.getCommissionDate() == null) {
                errorMsg.append("第").append(dataList.indexOf(excelDTO) + 1).append("行存在空字段");
            }
            // 校验来佣金额是否为正数
            if (excelDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                errorMsg.append("第").append(dataList.indexOf(excelDTO) + 1).append("行来佣金额必须为正数");
            }
            // 如果没有保单号，应收账款编号不能为空
            if (StringUtils.isBlank(excelDTO.getPolicyNo()) && StringUtils.isBlank(excelDTO.getReceivableNo())) {
                errorMsg.append("第").append(dataList.indexOf(excelDTO) + 1).append("行无保单号时应收账款编号不能为空");
            }
        }
        if (errorMsg.length() > 0) {
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), errorMsg.toString());
        }
    }

    /**
     * 1.处理导入的数据 2.比对预计来佣 3.保存到数据库
     */
    private List<Commission> processData(List<CommissionExcelDTO> dataList, String loginUserId) {
        List<Commission> entities = new ArrayList<>();
        for (CommissionExcelDTO data : dataList) {
            // 数据验证
            Commission entity = CommissionExcelDTO.convertToEntity(data, loginUserId);
            entities.add(entity);
        }

        // 保存来佣数据
        commissionService.saveBatch(entities);

        return entities;
    }

    /**
     * 生成实际出账（发佣）记录
     *
     * @param generateFortuneRequest
     * @param request
     * @return
     */
    @PostMapping("/generate/fortune")
    @Operation(summary = "生成实际出账（发佣）记录")
    public Result<Boolean> generateFortune(@RequestBody GenerateFortuneRequest generateFortuneRequest,
                                           HttpServletRequest request) {
        if (CollectionUtils.isEmpty(generateFortuneRequest.getCommissionBizIdList())) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "入账业务id列表不能为空");
        }
        return Result.success(commissionService.generateFortune(generateFortuneRequest));
    }

    /**
     * 新增入账
     *
     * @param customerAddRequest
     * @param request
     * @return
     */
    @Operation(summary = "新增入账")
    @PostMapping("/add")
    public Result<Boolean> addCommission(@RequestBody CommissionAddRequest customerAddRequest, HttpServletRequest request) {
        if (customerAddRequest == null || StringUtils.isBlank(customerAddRequest.getPolicyNo())) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "保单号不能为空");
        }
        return Result.success(commissionService.addCommission(customerAddRequest));
    }

    /**
     * 批量新增入账
     *
     * @param customerAddRequestList
     * @param request
     * @return
     */
    @Operation(summary = "批量新增入账")
    @PostMapping("/addBatch")
    public Result<List<Commission>> addCommissionBatch(@RequestBody List<CommissionAddRequest> customerAddRequestList, HttpServletRequest request) {
        if (CollectionUtils.isEmpty(customerAddRequestList)) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "入账数据不能为空");
        }
        return Result.success(commissionService.addCommissionBatch(customerAddRequestList));
    }

    /**
     * 同步到预计来佣
     *
     * @param addToExpectedCommissionRequest
     * @param request
     * @return
     */
    @Operation(summary = "同步到预计来佣")
    @PostMapping("/addToExpected")
    public Result<Boolean> addToExpected(@RequestBody AddToExpectedCommissionRequest addToExpectedCommissionRequest, HttpServletRequest request) {
        if (StringUtils.isBlank(addToExpectedCommissionRequest.getCommissionBizId())) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "来佣业务id不能为空");
        }
        return Result.success(commissionService.addToExpected(addToExpectedCommissionRequest));
    }

    /**
     * 来佣比对记录列表
     */
    @PostMapping("/compare/records")
    @Operation(summary = "来佣比对记录列表", description = "可根据实际来佣业务id、预计来佣业务id，查询来佣比对记录")
    public Result<Page<CommissionCompareRecord>> getCommissionCompareRecords(@RequestBody CommissionCompareRecordQueryRequest queryRequest) {
        if (StringUtils.isBlank(queryRequest.getCommissionBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "commissionBizId不能为空");
        }
        // 查询来佣比对记录
        QueryWrapper<CommissionCompareRecord> queryWrapper = new QueryWrapper<CommissionCompareRecord>();
        queryWrapper.eq(ObjectUtils.isNotEmpty(queryRequest.getCommissionBizId()), "commission_biz_id", queryRequest.getCommissionBizId());
        queryWrapper.eq(ObjectUtils.isNotEmpty(queryRequest.getCommissionExpectedBizId()), "commission_expected_biz_id", queryRequest.getCommissionExpectedBizId());
        queryWrapper.orderByAsc("create_time");
        Page<CommissionCompareRecord> page = commissionCompareRecordService.page(new Page<>(queryRequest.getPageNo(), queryRequest.getPageSize()),
                queryWrapper);

        return Result.success(page);
    }

    /**
     * 删除来佣
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public Result<Boolean> deleteCommission(@RequestBody CommissionDeleteRequest deleteRequest, HttpServletRequest request) {
        if (StringUtils.isBlank(deleteRequest.getCommissionBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        String commissionBizId = deleteRequest.getCommissionBizId();

        // 获取当前登录用户的ID
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 判断是否存在
        Commission oldCommission = commissionService.getByCommissionBizId(commissionBizId);
        if (oldCommission == null) {
            return Result.fail(ErrorCode.NOT_FOUND_ERROR.getCode(), ErrorCode.NOT_FOUND_ERROR.getMessage());
        }
        // 仅本人或管理员可删除
//        if (!oldCommission.getUserId().equals(loginUserId) && !userService.isAdmin(request)) {
//            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
//        }
        // 操作数据库
        boolean result = commissionService.removeById(oldCommission.getId());
        if (!result) {
            return Result.fail(ErrorCode.OPERATION_ERROR.getCode(), ErrorCode.OPERATION_ERROR.getMessage());
        }
        return Result.success(result);
    }

    /**
     * 更新保单来佣
     *
     * @param commissionUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @Operation(summary = "更新保单来佣信息")
    public Result<Boolean> updateCommission(@RequestBody CommissionUpdateRequest commissionUpdateRequest) {
        if (commissionUpdateRequest == null || commissionUpdateRequest.getCommissionBizId() == null) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        // 校验入参
        commissionService.validateCommissionUpdateRequest(commissionUpdateRequest);

        String lockKey = "COMMISSION_LOCK:" + commissionUpdateRequest.getCommissionBizId();
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 竞争锁
            boolean res = lock.tryLock(3, 15, TimeUnit.SECONDS);
            if (!res) {
                log.warn("获取分布式锁失败，commissionBizId: {}", commissionUpdateRequest.getCommissionBizId());
                throw new BusinessException("系统繁忙，请稍后重试");
            }
            // 更新来佣记录
            Result.success(commissionService.updateCommission(commissionUpdateRequest));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("更新来佣记录时，获取锁被中断", e);
            return Result.fail(ErrorCode.OPERATION_ERROR.getCode(), "更新来佣记录时，获取锁被中断");
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Result.success(true);
    }

    /**
     * 修改比对状态
     *
     * @param updateCompareStatusRequest
     * @return
     */
    @PostMapping("/updateCompareStatus")
    @Operation(summary = "修改比对状态")
    public Result<Boolean> updateCompareStatus(@RequestBody UpdateCompareStatusRequest updateCompareStatusRequest) {
        if (updateCompareStatusRequest == null) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        String commissionBizId = updateCompareStatusRequest.getCommissionBizId();
        if (StringUtils.isBlank(commissionBizId)) {
            log.error("来佣业务id不能为空：{}", commissionBizId);
            return Result.fail(ResultCode.NULL_ERROR.getCode(), "来佣业务id不能为空");
        }
        String status = updateCompareStatusRequest.getStatus();
        if (StringUtils.isBlank(status)) {
            log.error("来佣比对状态不能为空：{}", status);
            return Result.fail(ResultCode.NULL_ERROR.getCode(), "来佣比对状态不能为空");
        }

        // 查询旧数据
        Commission commission = commissionService.getByCommissionBizId(commissionBizId);
        if (commission == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "未找到该来佣记录");
        }

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 更新属性
        commission.setStatus(status);
        commission.setStatusDesc(updateCompareStatusRequest.getStatusDesc());
        commission.setUpdaterId(loginUserId);
        commission.setUpdateTime(new Date());
        // 执行更新
        commissionService.updateById(commission);

        return Result.success(true);
    }

    /**
     * 来佣操作记录列表
     *
     * @param queryRequest
     * @return
     */
    @PostMapping("/edit/records")
    @Operation(summary = "来佣操作记录列表")
    public Result<Page<CommissionEditRecord>> getCommissionEditRecords(@RequestBody CommissionEditRecordQueryRequest queryRequest) {
        if (StringUtils.isBlank(queryRequest.getCommissionBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "commissionBizId不能为空");
        }
        // 查询来佣操作记录
        Page<CommissionEditRecord> page = commissionEditRecordService.page(new Page<>(queryRequest.getPageNo(), queryRequest.getPageSize()), new LambdaQueryWrapper<CommissionEditRecord>()
                .eq(CommissionEditRecord::getCommissionBizId, queryRequest.getCommissionBizId())
                .orderByAsc(CommissionEditRecord::getCreateTime));
        return Result.success(page);
    }

    /**
     * 来佣检核页面-计算统计数据 总金额、总入账金额、待入账金额、已入账比例（已入账金额/总金额）、总保单数
     */
    @PostMapping("/statistics")
    @Operation(summary = "来佣检核页面-计算统计数据 总金额、总入账金额、待入账金额、已入账比例（已入账金额/总金额）、总保单数")
    public Result<CommissionStatisticsVO> getCommissionStatistics(@RequestBody CommissionStatisticsRequest commissionStatisticsRequest) {
        if (commissionStatisticsRequest == null) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        return Result.success(commissionService.getCommissionStatistics(commissionStatisticsRequest.getCommissionIds()));
    }


    /**
     * 根据 commissionBizId 获取保单来佣（封装类）
     *
     * @param commissionBizId
     * @return
     */
//    @GetMapping("/get/vo")
//    @Operation(summary = "根据 policyBizId 获取保单来佣详情")
//    public Result<PolicyFollowVO> getPolicyFollowByPolicyBizId(@RequestParam("commissionBizId") String commissionBizId, HttpServletRequest request) {
//        if (commissionBizId == null) {
//            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
//        }
//        // 查询数据库
//        PolicyFollow policyFollow = policyFollowService.getByPolicyBizId(policyBizId);
//        if (policyFollow == null) {
//            return Result.fail(ErrorCode.NOT_FOUND_ERROR.getCode(), ErrorCode.NOT_FOUND_ERROR.getMessage());
//        }
//
//        // 获取封装类
//        return Result.success(policyFollowService.getPolicyFollowVO(policyFollow));
//    }

    /**
     * 分页获取保单来佣列表（仅管理员可用）
     *
     * @param fnaQueryRequest
     * @return
     */
//    @PostMapping("/list/page")
//    public Result<Page<Customer>> listFnaByPage(@RequestBody FnaQueryRequest fnaQueryRequest) {
//        long current = fnaQueryRequest.getPageNo();
//        long size = fnaQueryRequest.getPageSize();
//        // 查询数据库
//        Page<Customer> fnaPage = policyFollowService.page(new Page<>(current, size),
//                policyFollowService.getQueryWrapper(fnaQueryRequest));
//        return Result.success(fnaPage);
//    }

    /**
     * 来佣检核页面-分页获取保单来佣列表（VO）
     *
     * @param commissionQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    @Operation(summary = "来佣检核页面-分页获取保单来佣列表")
    public Result<CommissionPageResponse> listCommissionByPage(@RequestBody CommissionQueryRequest commissionQueryRequest,
                                                               HttpServletRequest request) {
        long current = commissionQueryRequest.getPageNo();
        long size = commissionQueryRequest.getPageSize();

        // 查询列表数据
        QueryWrapper<Commission> queryWrapper = commissionService.getQueryWrapper(commissionQueryRequest);
        Page<Commission> commissionPage = commissionService.page(new Page<>(current, size),
                queryWrapper);

        // 查询统计数据
        List<Commission> commissionList = commissionService.list(queryWrapper);
        // 计算统计数据
        List<Long> commissionIds = commissionList.stream().map(Commission::getId).collect(Collectors.toList());
        CommissionStatisticsVO commissionStatisticsVO = commissionService.getCommissionStatistics(commissionIds);
        // 组装返回值
        CommissionPageResponse commissionPageResponse = new CommissionPageResponse();
        commissionPageResponse.setCommissionStatisticsVO(commissionStatisticsVO);
        commissionPageResponse.setPage(commissionService.getCommissionVOPage(commissionPage));
        return Result.success(commissionPageResponse);
    }

    /**
     * 入账记录
     *
     * @param commissionQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/pageByCommissionexpectedBizId")
    @Operation(summary = "入账记录")
    public Result<Page<CommissionVO>> pageByCommissionexpectedBizId(@RequestBody CommissionQueryRequest commissionQueryRequest,
                                                                    HttpServletRequest request) {
        if (ObjectUtils.isEmpty(commissionQueryRequest.getCommissionExpectedBizId()) && ObjectUtils.isEmpty(commissionQueryRequest.getReconciliationYearMonth())) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "commissionExpectedBizId、 reconciliationYearMonth 不能全部为空");
        }

        long current = commissionQueryRequest.getPageNo();
        long size = commissionQueryRequest.getPageSize();
        // 查询列表数据
        QueryWrapper<Commission> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("commission_expected_biz_id", commissionQueryRequest.getCommissionExpectedBizId());
        queryWrapper.eq("reconciliation_year_month", commissionQueryRequest.getReconciliationYearMonth());
        Page<Commission> commissionPage = commissionService.page(new Page<>(current, size),
                queryWrapper);
        // 组装返回值
        return Result.success(commissionService.getCommissionVOPage(commissionPage));
    }

}
