package org.dromara.zjk.annualInspection.annualInspectionAudit.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.zjk.annualInspection.annualInspectionAudit.domain.ZjkAnnualInspection;
import org.dromara.zjk.annualInspection.annualInspectionAudit.domain.bo.ZjkAnnualInspectionBo;
import org.dromara.zjk.annualInspection.annualInspectionAudit.domain.vo.ZjkAnnualInspectionVo;
import org.dromara.zjk.annualInspection.annualInspectionAudit.mapper.ZjkAnnualInspectionMapper;
import org.dromara.zjk.annualInspection.annualInspectionAudit.service.IZjkAnnualInspectionService;
import org.dromara.zjk.annualInspection.annualInspectionRecords.domain.ZjkAnnualInspectionRecords;
import org.dromara.zjk.annualInspection.annualInspectionRecords.mapper.ZjkAnnualInspectionRecordsMapper;
import org.dromara.zjk.domain.ZjkExpert;
import org.dromara.zjk.enums.AnnualInspectionStatus;
import org.dromara.zjk.enums.AnnualInspectionTypeEnum;
import org.dromara.zjk.mapper.ZjkExpertMapper;
import org.dromara.zjk.service.IZjkExpertService;
import org.dromara.zjk.utils.IdCardUtils;
import org.dromara.zjk.utils.NumberGenerator;
import org.dromara.zjk.zwy.client.HmacClient;
import org.dromara.zjk.zwy.utils.ObjectHashGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 年检管理Service业务层处理
 *
 * @author zhangzhou
 * @date 2024-11-13
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class ZjkAnnualInspectionServiceImpl implements IZjkAnnualInspectionService {

    private final ZjkAnnualInspectionMapper zjkAnnualInspectionMapper;

    private final ZjkAnnualInspectionRecordsMapper zjkAnnualInspectionRecordsMapper;

    private final ZjkExpertMapper zjkExpertMapper;
    @Autowired
    private IZjkExpertService zjkExpertService;

    private final HmacClient hmacClient;

    /**
     * 查询年检管理
     *
     * @param id 主键
     * @return 年检管理
     */
    @Override
    public ZjkAnnualInspectionVo queryById(Long id){
        return zjkAnnualInspectionMapper.selectVoById(id);
    }

    /**
     * 分页查询年检管理列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 年检管理分页列表
     */
    @Override
    public TableDataInfo<ZjkAnnualInspectionVo> queryPageList(ZjkAnnualInspectionBo bo, PageQuery pageQuery) {
        Page<ZjkAnnualInspectionVo> result = zjkAnnualInspectionMapper.queryAnnualInspection(pageQuery.build(), bo);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的年检管理列表
     *
     * @param bo 查询条件
     * @return 年检管理列表
     */
    @Override
    public List<ZjkAnnualInspectionVo> queryList(ZjkAnnualInspectionBo bo) {
        LambdaQueryWrapper<ZjkAnnualInspection> lqw = buildQueryWrapper(bo);
        return zjkAnnualInspectionMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ZjkAnnualInspection> buildQueryWrapper(ZjkAnnualInspectionBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ZjkAnnualInspection> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, ZjkAnnualInspection::getUserId, bo.getUserId());
        lqw.eq(bo.getExpertId() != null, ZjkAnnualInspection::getExpertId, bo.getExpertId());
        lqw.like(StringUtils.isNotBlank(bo.getAnnualInspectionSn()), ZjkAnnualInspection::getAnnualInspectionSn, bo.getAnnualInspectionSn());
        lqw.like(StringUtils.isNotBlank(bo.getAnnualInspectionName()), ZjkAnnualInspection::getAnnualInspectionName, bo.getAnnualInspectionName());
        lqw.eq(StringUtils.isNotBlank(bo.getAnnualInspectionType()), ZjkAnnualInspection::getAnnualInspectionType, bo.getAnnualInspectionType());
        lqw.eq(StringUtils.isNotBlank(bo.getAnnualInspectionStatus()), ZjkAnnualInspection::getAnnualInspectionStatus, bo.getAnnualInspectionStatus());
        lqw.between(params.get("beginAuditTime") != null && params.get("endAuditTime") != null,
            ZjkAnnualInspection::getAuditTime ,params.get("beginAuditTime"), params.get("endAuditTime"));
        return lqw;
    }

    /**
     * 新增年检管理
     *
     * @param bo 年检管理
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(ZjkAnnualInspectionBo bo) {
        ZjkAnnualInspection add = MapstructUtils.convert(bo, ZjkAnnualInspection.class);
        validEntityBeforeSave(add);
        boolean flag = zjkAnnualInspectionMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改年检管理
     *
     * @param bo 年检管理
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(ZjkAnnualInspectionBo bo) {
        ZjkAnnualInspection update = MapstructUtils.convert(bo, ZjkAnnualInspection.class);
        validEntityBeforeSave(update);
        return zjkAnnualInspectionMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ZjkAnnualInspection entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除年检管理信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return zjkAnnualInspectionMapper.deleteByIds(ids) > 0;
    }

    @Override
    public void batchAnnualInspection(ZjkAnnualInspectionBo zjkAnnualInspectionBo) {
        // 获取当前年检记录是否存在
        for (Long id : zjkAnnualInspectionBo.getIds()) {
            ZjkAnnualInspection zjkAnnualInspection =  zjkAnnualInspectionMapper.selectById(id);
            if (zjkAnnualInspection == null) {
                throw new ServiceException("数据异常");
            }
            ZjkExpert zjkExpert = zjkExpertMapper.selectById(zjkAnnualInspection.getExpertId());
            if (zjkExpert == null) {
                throw new ServiceException("专家数据异常");
            }
            zjkAnnualInspection.setAuditTime(new Date());
            zjkAnnualInspection.setAnnualInspectionStatus(zjkAnnualInspectionBo.getAnnualInspectionStatus());
            ZjkAnnualInspectionRecords zjkAnnualInspectionRecords = createAnnualInspectionRecords(zjkExpert, zjkAnnualInspection,zjkAnnualInspection.getAnnualInspectionType());
            zjkAnnualInspectionRecordsMapper.insert(zjkAnnualInspectionRecords);
            // 进行 年检记录新增
            zjkAnnualInspectionBo.setAuditTime(zjkAnnualInspection.getAuditTime());
            try {
                // 只对年检状态生成 HMAC
                Map<String, Object> hmacMap = new HashMap<>();
                hmacMap.put("expertId", zjkExpert.getExpertId()); // 加上 expertId 做唯一标识
                hmacMap.put("annualInspectionStatus", zjkAnnualInspectionBo.getAnnualInspectionStatus());

                // 本地哈希再编码（或改成远程 HMAC 接口）
                String hash = ObjectHashGenerator.generateHash(hmacMap);
                String base64 = Base64.getEncoder().encodeToString(hash.getBytes(StandardCharsets.UTF_8));
                 String hmac = hmacClient.calculateHmac(base64);
//                String hmac = base64;

                zjkAnnualInspection.setHmac(hmac);
                log.info("年检HMAC生成成功 expertId:{} HMAC:{}", zjkExpert.getExpertId(), hmac);
            } catch (Exception e) {
                log.error("年检HMAC生成失败 expertId:{}", zjkExpert.getExpertId(), e);
            }
            zjkAnnualInspectionMapper.batchAnnualInspection(zjkAnnualInspectionBo);

        }
    }

    /**
     * 自动年检定时任务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeAnnualInspection() {
        // 获取开始时间
        long startTime = System.currentTimeMillis();
        // 用户修改集合
        List<ZjkExpert> updateExpert = new ArrayList<>();
        // 年检审核集合
        List<ZjkAnnualInspection> zjkAnnualInspectionList = new ArrayList<>();
        // 年检记录集合
        List<ZjkAnnualInspectionRecords> zjkAnnualInspectionRecordsList = new ArrayList<>();
        // 获取所有的专家信息
        List<ZjkExpert> zjkExpertList = zjkExpertMapper.selectExpertAllList();

        for (ZjkExpert expert : zjkExpertList) {
            if (expert.getCreditCount() == null){
                continue;
            }
            // 条件：年龄大于 65 或者信誉分低于 90 时需要进行人工年检。
            boolean isEligibleForInspection = !IdCardUtils.isAgeUnder65(expert.getIdCard()) || expert.getCreditCount() < 90;

            // 如果 `isEligibleForInspection` 为 true，则状态为 "等待年检"（AWAITING_ANNUAL_INSPECTION）。否则，状态为 "通过"（PASSED）。
            String status = isEligibleForInspection
                ? AnnualInspectionStatus.AWAITING_ANNUAL_INSPECTION.getCode()  // 年龄大于 65 或信誉分不足 90 时，设置为 "等待年检" 状态
                : AnnualInspectionStatus.PASSED.getCode();                      // 否则，设置为 "通过" 状态

            // 如果 `isEligibleForInspection` 为 true，则类型为 "人工年检"（MANUAL_ANNUAL_INSPECTION）。 否则，类型为 "自动年检"（AUTOMATIC_ANNUAL_INSPECTION）。
            String type = isEligibleForInspection
                ? AnnualInspectionTypeEnum.MANUAL_ANNUAL_INSPECTION.getCode()   // 年龄大于 65 或信誉分不足 90 时，设置为 "人工年检" 类型
                : AnnualInspectionTypeEnum.AUTOMATIC_ANNUAL_INSPECTION.getCode(); // 否则，设置为 "自动年检" 类型

            // 创建年检审批和年检记录
            ZjkAnnualInspection zjkAnnualInspection = createAnnualInspection(expert, status,type);
            ZjkAnnualInspectionRecords zjkAnnualInspectionRecords = createAnnualInspectionRecords(expert, zjkAnnualInspection,type);

            if(!isEligibleForInspection){
                zjkAnnualInspection.setReason("自动年检通过");
            }
            zjkAnnualInspectionList.add(zjkAnnualInspection);
            zjkAnnualInspectionRecordsList.add(zjkAnnualInspectionRecords);

            // 更新专家信息
            expert.setAnnualInspectionStatus(status);
            expert.setAnnualInspectionTime(DateUtils.getFirstAndLastDayOfMonth(12));
            updateExpert.add(expert);
            try {
                // 只对年检状态生成 HMAC
                Map<String, Object> hmacMap = new HashMap<>();
                hmacMap.put("expertId", expert.getExpertId()); // 建议加上 expertId 做唯一标识
                hmacMap.put("annualInspectionStatus", status);

                // 本地哈希再编码（或改成远程 HMAC 接口）
                String hash = ObjectHashGenerator.generateHash(hmacMap);
                String base64 = Base64.getEncoder().encodeToString(hash.getBytes(StandardCharsets.UTF_8));
                 String hmac = hmacClient.calculateHmac(base64);
//                String hmac = base64;

                zjkAnnualInspection.setHmac(hmac);
                log.info("年检HMAC生成成功 expertId:{} HMAC:{}", expert.getExpertId(), hmac);
            } catch (Exception e) {
                log.error("年检HMAC生成失败 expertId:{}", expert.getExpertId(), e);
            }

        }

        // 此处可以批量插入或更新操作 zjkAnnualInspectionList, zjkAnnualInspectionRecordsList, updateExpert
        zjkAnnualInspectionMapper.insertBatch(zjkAnnualInspectionList);
        zjkAnnualInspectionRecordsMapper.insertBatch(zjkAnnualInspectionRecordsList);
        if (CollUtil.isNotEmpty(updateExpert)){
            updateExpert.forEach(expert -> expert.setIdCard(null));
            updateExpert.forEach(expert -> expert.setContact(null));
            zjkExpertMapper.updateBatchById(updateExpert);
        }
        // 如果需要重新计算 HMAC
        for (ZjkExpert expert : updateExpert) {
            zjkExpertService.refreshHmac(expert.getExpertId());
        }
        long endTime = System.currentTimeMillis();
        // 计算方法执行时间（毫秒）
        long duration = endTime - startTime;
        log.info("自动年检方法执行时间: " + duration + " 毫秒");
    }

    private ZjkAnnualInspection createAnnualInspection(ZjkExpert expert, String status,String type) {
        ZjkAnnualInspection zjkAnnualInspection = new ZjkAnnualInspection();
        zjkAnnualInspection.setUserId(expert.getUserId());
        zjkAnnualInspection.setExpertId(expert.getExpertId());
        zjkAnnualInspection.setAnnualInspectionSn(NumberGenerator.generateUniqueNumber("NJSHBH", 5));
        zjkAnnualInspection.setAnnualInspectionName(DateUtils.dateTimeNow(DateUtils.YYYY) + "年度年检资料");
        zjkAnnualInspection.setAnnualInspectionType(type);
        zjkAnnualInspection.setAnnualInspectionStatus(status);
        zjkAnnualInspection.setTenantId(expert.getTenantId());
        zjkAnnualInspection.setAuditTime(new Date());
        zjkAnnualInspection.setCreateDept(expert.getCreateDept());
        return zjkAnnualInspection;
    }

    private ZjkAnnualInspectionRecords createAnnualInspectionRecords(ZjkExpert expert, ZjkAnnualInspection inspection,String type) {
        ZjkAnnualInspectionRecords zjkAnnualInspectionRecords = new ZjkAnnualInspectionRecords();
        zjkAnnualInspectionRecords.setUserId(expert.getUserId());
        zjkAnnualInspectionRecords.setExpertId(expert.getExpertId());
        zjkAnnualInspectionRecords.setRecordsSn(NumberGenerator.generateUniqueNumber("NJJLBH", 5));
        zjkAnnualInspectionRecords.setAnnualInspectionSn(inspection.getAnnualInspectionSn());
        zjkAnnualInspectionRecords.setAnnualInspectionName(inspection.getAnnualInspectionName());
        zjkAnnualInspectionRecords.setAuditType(type);
        zjkAnnualInspectionRecords.setAnnualInspectionStatus(inspection.getAnnualInspectionStatus());
        zjkAnnualInspectionRecords.setAuditTime(inspection.getAuditTime());
        zjkAnnualInspectionRecords.setTenantId(expert.getTenantId());
        zjkAnnualInspectionRecords.setCreateDept(expert.getCreateDept());
        return zjkAnnualInspectionRecords;
    }
}
