package com.yeejoin.amos.boot.module.jyjc.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.bo.CompanyBo;
import com.yeejoin.amos.boot.biz.common.entity.DataDictionary;
import com.yeejoin.amos.boot.biz.common.service.impl.DataDictionaryServiceImpl;
import com.yeejoin.amos.boot.module.common.api.constant.TZSCommonConstant;
import com.yeejoin.amos.boot.module.common.api.dto.BaseEnterpriseCertDto;
import com.yeejoin.amos.boot.module.jyjc.api.common.BizCommonConstant;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcInspectionHistory;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcOpeningApplication;
import com.yeejoin.amos.boot.module.jyjc.api.enums.OpenBizTypeEnumV2;
import com.yeejoin.amos.boot.module.jyjc.api.model.JyjcBizManageModel;
import com.yeejoin.amos.boot.module.jyjc.api.model.JyjcOpeningApplicationModel;
import com.yeejoin.amos.boot.module.ymt.api.entity.BaseUnitLicence;
import com.yeejoin.amos.boot.module.ymt.api.enums.FlowStatusEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.BaseUnitLicenceMapper;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.DictionarieValueModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.typroject.tyboot.core.rdbms.orm.entity.BaseEntity;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.yeejoin.amos.boot.module.jyjc.api.common.BizCommonConstant.JYJGFL_DICT_KEY;
import static com.yeejoin.amos.boot.module.jyjc.api.common.BizCommonConstant.OPEN_REGION_CODE_DICT_KEY;

@Service
@RequiredArgsConstructor
@Slf4j
public class JyjcBizManageServiceImpl {

    private static final String TY = "停用";

    private static final String QY = "启用";

    private final JyjcOpeningApplicationServiceImpl openingApplicationService;

    private final JyjcInspectionHistoryServiceImpl inspectionHistoryService;

    private final RedissonClient redissonClient;

    private final BaseUnitLicenceMapper baseUnitLicenceMapper;

    private final DataDictionaryServiceImpl dictionaryService;


    public IPage<?> pageList(Page<JyjcBizManageModel> page, CompanyBo company, JyjcOpeningApplicationModel model) {
        Page<JyjcOpeningApplication> pageApp = new Page<>(page.getCurrent(), page.getSize());
        LambdaQueryWrapper<JyjcOpeningApplication> queryWrapper = new LambdaQueryWrapper<>();
        this.setIdentify(company, queryWrapper);
        queryWrapper.eq(JyjcOpeningApplication::getStatus, FlowStatusEnum.TO_BE_FINISHED.getName());
        queryWrapper.like(StringUtils.isNotEmpty(model.getUnitCodeName()), JyjcOpeningApplication::getUnitCodeName, model.getUnitCodeName());
        queryWrapper.eq(StringUtils.isNotEmpty(model.getOpenBizType()), JyjcOpeningApplication::getOpenBizType, model.getOpenBizType());
        if (StringUtils.isNotEmpty(model.getDetectionRegionName())) {
            queryWrapper.and(wp -> wp.like(JyjcOpeningApplication::getDetectionRegion, model.getDetectionRegionName()).and(w -> w.isNull(JyjcOpeningApplication::getExclusionRegion).or().notLike(JyjcOpeningApplication::getExclusionRegion, model.getDetectionRegionName())));
        }
        queryWrapper.orderByDesc(BaseEntity::getSequenceNbr);
        IPage<JyjcOpeningApplication> res = openingApplicationService.page(pageApp, queryWrapper);
        Map<String, String> detectionRegionCodeNameMap = getDetectionRegionCodeNameMap();
        Map<String, String> agencyClassifyNameCodeNameMap = getAgencyClassifyNameCodeNameMap();
        List<JyjcBizManageModel> manageModels = res.getRecords().stream().map(r -> {
            JyjcBizManageModel bizManageModel = BeanUtil.copyProperties(r, JyjcBizManageModel.class);
            setAreaStatusByIdentify(company, r, bizManageModel);
            bizManageModel.setCompanyCode(company.getCompanyCode());
            bizManageModel.setOpenBizTypeName(Optional.of(OpenBizTypeEnumV2.getOneByCode(bizManageModel.getOpenBizType())).map(OpenBizTypeEnumV2::getDescription).orElse(""));
            bizManageModel.setDetectionRegionName(this.buildRegionName(detectionRegionCodeNameMap, r.getDetectionRegion()));
            bizManageModel.setExclusionRegionName(this.buildRegionName(detectionRegionCodeNameMap, r.getExclusionRegion()));
            bizManageModel.setAgencyClassifyName(agencyClassifyNameCodeNameMap.getOrDefault(bizManageModel.getAgencyClassify(), ""));
            bizManageModel.setLogUserEnable(!Optional.ofNullable(r.getExclusionRegion()).orElse(new ArrayList<>()).contains(company.getCompanyCode()));
            return bizManageModel;
        }).collect(Collectors.toList());
        page.setRecords(manageModels);
        page.setTotal(res.getTotal());
        return page;
    }

    private Map<String, String> getAgencyClassifyNameCodeNameMap() {
        List<DataDictionary> dict = dictionaryService.getByType(JYJGFL_DICT_KEY);
        return dict.stream().collect(Collectors.toMap(DataDictionary::getCode, DataDictionary::getName));
    }

    private static Map<String, String> getDetectionRegionCodeNameMap() {
        List<DictionarieValueModel> detectionRegions = Systemctl.dictionarieClient.dictValues(OPEN_REGION_CODE_DICT_KEY).getResult();
        return detectionRegions.stream().collect(Collectors.toMap(DictionarieValueModel::getDictDataKey, DictionarieValueModel::getDictDataValue));
    }

    private String buildRegionName(Map<String, String> detectionRegionCodeNameMap, List<String> regionCodes) {
        return Optional.ofNullable(regionCodes).map(r -> r.stream().map(detectionRegionCodeNameMap::get).collect(Collectors.joining(","))).orElse("");
    }

    private static void setAreaStatusByIdentify(CompanyBo company, JyjcOpeningApplication r, JyjcBizManageModel bizManageModel) {
        if (TZSCommonConstant.SHAN_XI_REGION_CODE.equals(company.getCompanyCode())) {
            bizManageModel.setAreaStatus(QY);
        } else {
            if (r.getExclusionRegion() != null && r.getExclusionRegion().contains(company.getCompanyCode())) {
                bizManageModel.setAreaStatus(TY);
            } else {
                bizManageModel.setAreaStatus(QY);
            }
        }
    }

    private void setIdentify(CompanyBo company, LambdaQueryWrapper<JyjcOpeningApplication> queryWrapper) {
        if (!TZSCommonConstant.SHAN_XI_REGION_CODE.equals(company.getCompanyCode())) {
            queryWrapper.and(w ->
                    w.like(JyjcOpeningApplication::getDetectionRegion, company.getCompanyCode())
                            .or()
                            .like(JyjcOpeningApplication::getExclusionRegion, company.getCompanyCode()));
        }
    }

    public Boolean enable(String appSeq, CompanyBo company) {
        log.info("启用企业区域权限, appSeq: {}, company: {}", appSeq, company.getCompanyCode());
        String lockKey = "app:region:lock:" + appSeq;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：多个人同时操作一个单据
            if (!isLocked) {
                throw new BadRequest("其他机构同时在操作该业务，请稍后重试！");
            }
            JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
            List<String> exclusionRegion = openingApplication.getExclusionRegion();
            if (exclusionRegion != null) {
                exclusionRegion.remove(company.getCompanyCode());
            }
            openingApplication.setExclusionRegion(exclusionRegion);
            openingApplicationService.saveOrUpdate(openingApplication);

        } catch (InterruptedException e) {
            log.error("启用企业区域失败：{}", e.getMessage(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    public Boolean disable(String appSeq, CompanyBo company) {
        log.info("禁用企业区域权限, appSeq: {}, company: {}", appSeq, company.getCompanyCode());
        String lockKey = "app:region:lock:" + appSeq;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：解决并发问题：多个人同时操作一个单据
            if (!isLocked) {
                throw new BadRequest("其他机构同时在操作该业务，请稍后重试！");
            }
            JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
            List<String> exclusionRegion = openingApplication.getExclusionRegion();
            Set<String> exclusionRegionSet = Optional.ofNullable(exclusionRegion).map(HashSet::new).orElse(new HashSet<>());
            exclusionRegionSet.add(company.getCompanyCode());
            openingApplication.setExclusionRegion(new ArrayList<>(exclusionRegionSet));
            openingApplicationService.saveOrUpdate(openingApplication);
        } catch (InterruptedException e) {
            log.error("禁用企业区域失败：{}", e.getMessage(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    public Boolean licenceEdit(String appSeq, List<BaseUnitLicence> licences) {
        JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
        if (!openingApplication.getStatus().equals(FlowStatusEnum.TO_BE_FINISHED.getName())) {
            throw new BadRequest("开通状态已经变化，请刷新后重试！");
        }
        licences.forEach(licence -> {
            LambdaUpdateWrapper<BaseUnitLicence> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(com.yeejoin.amos.boot.biz.common.entity.BaseEntity::getSequenceNbr, licence.getSequenceNbr());
            updateWrapper.set(BaseUnitLicence::getLicenceState, licence.getLicenceState());
            baseUnitLicenceMapper.update(null, updateWrapper);
        });
        return Boolean.TRUE;
    }

    public Map<String, Object> licenceDetail(String appSeq) {
        Map<String, Object> res = new HashMap<>();
        List<BaseUnitLicence> baseUnitLicences = new ArrayList<>();
        JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
        JyjcInspectionHistory history = inspectionHistoryService.getBySSeq(appSeq);
        if (history != null) {
            JSONObject hisData = history.getHistoryData();
            List<BaseEnterpriseCertDto> certDtos = hisData.getJSONArray(BizCommonConstant.UNIT_LICENCE_KEY).toJavaList(BaseEnterpriseCertDto.class);
            certDtos.forEach(certDto -> {
                List<Long> certSeqList = openingApplicationService.getCertFromHisData(openingApplication, certDto);
                if (!certSeqList.isEmpty()) {
                    List<BaseUnitLicence> baseUnitLicenceOne = baseUnitLicenceMapper.selectList(new LambdaQueryWrapper<BaseUnitLicence>().in(BaseUnitLicence::getEnterpriseCertSeq, certSeqList));
                    baseUnitLicences.addAll(baseUnitLicenceOne);
                }
            });
        }
        List<Map<String, Object>> licences = baseUnitLicences.stream().map(e -> {
            Map<String, Object> map = new HashMap<>();
            BeanUtil.copyProperties(e, map);
            map.put("licenceState", String.valueOf(e.getLicenceState()));
            map.put("qualificationCertificateAttachment", JSONObject.parseArray(e.getQualificationCertificateAttachment()));
            return map;
        }).collect(Collectors.toList());
        res.put("licences", licences);
        return res;
    }

    public Boolean expireDateEdit(String appSeq, String newExpiryDate) {
        log.info("省局企业业务有效期调整权限, appSeq: {}, newExpiryDate: {}", appSeq, newExpiryDate);
        String lockKey = "app:region:lock:" + appSeq;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：解决并发问题：多个人同时操作一个单据
            if (!isLocked) {
                throw new BadRequest("其他机构同时在操作该业务，请稍后重试！");
            }
            LambdaUpdateWrapper<JyjcOpeningApplication> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(BaseEntity::getSequenceNbr, appSeq);
            updateWrapper.set(JyjcOpeningApplication::getExpiryDate, DateUtil.parseDate(newExpiryDate));
            openingApplicationService.update(updateWrapper);
        } catch (InterruptedException e) {
            log.error("禁用企业区域失败：{}", e.getMessage(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    public Boolean detectionRegionEdit(String appSeq, String newDetectionRegion) {
        log.info("省局企业区域调整权限, appSeq: {}, newDetectionRegion: {}", appSeq, newDetectionRegion);
        String lockKey = "app:region:lock:" + appSeq;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：解决并发问题：多个人同时操作一个单据
            if (!isLocked) {
                throw new BadRequest("其他机构同时在操作该业务，请稍后重试！");
            }
            JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
            openingApplication.setDetectionRegion(Arrays.stream(newDetectionRegion.split(",")).collect(Collectors.toList()));
            if (openingApplication.getExclusionRegion() != null) {
                // 如果省局维护后的区域，去掉了一部分，则把市局停用区域字段也去掉
                openingApplication.setExclusionRegion(openingApplication.getExclusionRegion().stream().filter(e -> openingApplication.getDetectionRegion().contains(e)).collect(Collectors.toList()));
            }
            openingApplicationService.saveOrUpdate(openingApplication);
        } catch (InterruptedException e) {
            log.error("禁用企业区域失败：{}", e.getMessage(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    public JyjcBizManageModel getBasicInfo(String appSeq) {
        JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
        JyjcBizManageModel manageModel = BeanUtil.copyProperties(openingApplication, JyjcBizManageModel.class);
        manageModel.setNewDetectionRegion(openingApplication.getDetectionRegion());
        return manageModel;
    }

    public Map<String, Object> getOne(String appSeq) {
        Map<String, Object> res = new HashMap<>();
        JyjcOpeningApplication openingApplication = openingApplicationService.getById(appSeq);
        JyjcInspectionHistory jyjcInspectionHistory = inspectionHistoryService.getBySSeq(openingApplication.getSequenceNbr());
        if (jyjcInspectionHistory != null) {
            JSONObject hisData = jyjcInspectionHistory.getHistoryData();
            BeanUtil.copyProperties(hisData, res);
            res.remove("baseUnitLicences");
            res.put("licences", getLicenceInfo(hisData, openingApplication));
        }
        res.put("detectionRegion", openingApplication.getDetectionRegion());
        res.put("exclusionRegion", openingApplication.getExclusionRegion());
        res.put("status", openingApplication.getStatus());
        res.put("applicationSeq", openingApplication.getApplicationSeq());
        res.put("acceptDate", openingApplication.getAcceptDate());
        return res;
    }

    private List<Map<String, Object>> getLicenceInfo(JSONObject hisData, JyjcOpeningApplication openingApplication) {
        List<BaseUnitLicence> baseUnitLicences = new ArrayList<>();
        List<BaseEnterpriseCertDto> certDtos = hisData.getJSONArray(BizCommonConstant.UNIT_LICENCE_KEY).toJavaList(BaseEnterpriseCertDto.class);
        certDtos.forEach(certDto -> {
            List<Long> certSeqList = openingApplicationService.getCertFromHisData(openingApplication, certDto);
            if (!certSeqList.isEmpty()) {
                List<BaseUnitLicence> baseUnitLicenceOne = baseUnitLicenceMapper.selectList(new LambdaQueryWrapper<BaseUnitLicence>().in(BaseUnitLicence::getEnterpriseCertSeq, certSeqList));
                baseUnitLicences.addAll(baseUnitLicenceOne);
            }
        });
        return baseUnitLicences.stream().map(e -> {
            Map<String, Object> map = new HashMap<>();
            BeanUtil.copyProperties(e, map);
            map.put("licenceState", String.valueOf(e.getLicenceState()));
            map.put("qualificationCertificateAttachment", JSONObject.parseArray(e.getQualificationCertificateAttachment()));
            return map;
        }).collect(Collectors.toList());
    }
}
