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

import com.alibaba.fastjson.JSONArray;
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.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.bo.CompanyBo;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.controller.BaseController;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.biz.common.utils.SnowflakeIdUtil;
import com.yeejoin.amos.boot.module.tcm.api.dto.RiskProcessesDto;
import com.yeejoin.amos.boot.module.tcm.api.dto.RiskReportDto;
import com.yeejoin.amos.boot.module.tcm.api.entity.RiskProcesses;
import com.yeejoin.amos.boot.module.tcm.api.entity.RiskReport;
import com.yeejoin.amos.boot.module.tcm.api.enums.*;
import com.yeejoin.amos.boot.module.tcm.api.mapper.RiskReportMapper;
import com.yeejoin.amos.boot.module.tcm.api.service.IRiskReportService;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.AgencyUserModel;
import com.yeejoin.amos.feign.privilege.model.CompanyModel;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 风险报送表服务实现类
 *
 * @author system_generator
 * @date 2025-08-08
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RiskReportServiceImpl extends BaseService<RiskReport, RiskReport, RiskReportMapper> implements IRiskReportService {

    /**
     * 风险类型
     */
    public static final String RISK_TYPE = "RISK_TYPE";
    private static final String LOCK_KEY = "lock:sequence:risk";
    private static final String SEQUENCE_TYPE_AF = "%03d";
    private static final String SEQUENCE_KEY = "SXBS";
    public static final String COMPANY_CODE = "companyCode";
    public static final String COMPANY_NAME = "companyName";
    public static final String COMPANY_ORG_CODE = "companyOrgCode";
    public static final String COMPANY_SEQ = "companySeq";

    private final SnowflakeIdUtil sequence;
    private final RedisUtils redisUtils;
    private final RiskReportMapper riskReportMapper;
    private final RiskProcessesServiceImpl riskProcessesService;
    private final RedissonClient redissonClient;
    private final RedisTemplate<String, String> redisTemplate;


    /**
     * @return 当前登录用户信息
     */
    protected ReginParams getSelectedOrgInfo() {
        return JSONObject.parseObject(redisUtils.get(RedisKey.buildReginKey(RequestContext.getExeUserId(), RequestContext.getToken())).toString(), ReginParams.class);
    }

    /**
     * 包含全部监管机构及企业的分页列表
     *
     * @param current     当前页
     * @param size        每页数量
     * @param isAdmin     是否是监管机构
     * @param companyName 公司名称
     * @return 列表
     */
    @Override
    public Page<Map<String, String>> allAgencyWithPage(int current, int size, boolean isAdmin, String companyName) {
        Page<Map<String, String>> page = new Page<>();
        page.setCurrent(current);
        page.setSize(size);
        return riskReportMapper.allAgencyWithPage(page, isAdmin, companyName);
    }

    /**
     * 查询企业的监管机构
     *
     * @param enterSeq 企业seq
     * @return result
     */
    @Override
    public JSONObject superviseOrgForEnter(String enterSeq) {
        CompanyModel enter = Privilege.companyClient.seleteOne(Long.valueOf(enterSeq)).getResult();
        CompanyModel companyModel = Privilege.companyClient.seleteOne(enter.getParentId()).getResult();
        return new JSONObject().fluentPut("key", companyModel.getSequenceNbr())
                .fluentPut("value", companyModel.getSequenceNbr() + "_" + companyModel.getCompanyName())
                .fluentPut("label", companyModel.getCompanyName());
    }

    /**
     * 根据是否有权限返回风险类型列表
     *
     * @return response
     */
    @Override
    public List<DictionarieValueModel> riskTypeByPermission() {
        List<DictionarieValueModel> result = new ArrayList<>();
        AtomicBoolean hasPermission = new AtomicBoolean(false);
        AgencyUserModel currentUserInfo = Privilege.agencyUserClient.getme().getResult();
        currentUserInfo.getOrgRoleSeqs().values().forEach(roleSeqs -> {
            roleSeqs.forEach(roleSeq -> {
                if (roleSeq.equals(PersonManageRoleEnum.fxbsy.getId())) {
                    hasPermission.set(true);
                }
            });
        });
        // 有权限
        if (hasPermission.get()) {
            return Systemctl.dictionarieClient.dictValues(RISK_TYPE).getResult();
        }
        // 无报送权限
        return Collections.emptyList();
    }

    /**
     * 分页查询
     */
    public Page<RiskReport> queryForRiskReportPage(RiskReportDto reportDto, int current, int size) {
        Page<RiskReport> page = new Page<>();
        page.setCurrent(current);
        page.setSize(size);
        CompanyBo company = getSelectedOrgInfo().getCompany();
        if (company.getLevel().equals(BaseController.COMPANY_TYPE_COMPANY)) {
            reportDto.setUnitType(BaseController.COMPANY_TYPE_COMPANY);
        } else {
            reportDto.setUnitType(BaseController.COMPANY_TYPE_SUPERVISION);
        }
        reportDto.setReportCompanyCode(company.getCompanyCode());
        reportDto.setRiskCompanyCode(company.getCompanyCode());
        reportDto.setRiskDisposalUnitOrgCode(company.getCompanyCode());
        reportDto.setCreateUserId(RequestContext.getExeUserId());
        return riskReportMapper.queryForRiskReportPage(page, reportDto);
    }

    /**
     * 风险上报
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RiskReport saveOrUpdateReport(RiskReportDto dto) {
        CompanyBo company = getSelectedOrgInfo().getCompany();
        AgencyUserModel userModel = getSelectedOrgInfo().getUserModel();
        Date now = new Date();
        Boolean submitFlag = dto.getSubmitFlag();
        String[] riskCompany = dto.getRiskCompany().split("_");
        Map<String, String> riskCompanyMap = this.getCompanyBySeq(riskCompany[0]);
        String[] riskDisposalUnit = dto.getRiskDisposalUnit().split("_");
        Map<String, String> riskDisposalUnitMap = this.getCompanyBySeq(riskDisposalUnit[0]);
        RiskReport riskReport = RiskReport.builder()
                .reportNo(ObjectUtils.isEmpty(dto.getSequenceNbr()) ? generateReportNo() : null)
                .riskTypeCode(dto.getRiskTypeCode())
                .riskTypeName(getRiskTypeNameByCode(dto.getRiskTypeCode()))
                .reportCompanySeq(company.getSequenceNbr().toString())
                .reportCompanyCode(company.getCompanyCode())
                .reportCompanyName(company.getCompanyName())
                .riskCompanySeq(String.valueOf(riskCompanyMap.get(COMPANY_SEQ)))
                .riskCompanyCode(riskCompanyMap.get(COMPANY_CODE))
                .riskCompanyName(riskCompanyMap.get(COMPANY_NAME))
                .riskDisposalUnitSeq(String.valueOf(riskDisposalUnitMap.get(COMPANY_SEQ)))
                .riskDisposalUnitCode(riskDisposalUnitMap.get(COMPANY_CODE))
                .riskDisposalUnitName(riskDisposalUnitMap.get(COMPANY_NAME))
                .riskDisposalUnitOrgCode(riskDisposalUnitMap.get(COMPANY_ORG_CODE))
                .allRiskDisposalUnitOrgCode(this.buildAllRiskDisposalUnitOrgCode(riskDisposalUnitMap.get(COMPANY_ORG_CODE), dto.getSequenceNbr()))
                .riskEquipRecord(null)
                .operationUserId(userModel.getUserId())
                .riskMainTitle(dto.getRiskMainTitle())
                .riskMainContent(dto.getRiskMainContent())
                .riskMainFile(ObjectUtils.isEmpty(dto.getRiskMainFile()) ? null : JSONObject.toJSONString(JSONArray.parseObject(dto.getRiskMainFile())))
                .reportUserId(userModel.getUserId())
                .reportUserName(userModel.getRealName())
                .reportUserPhone(userModel.getLandlinePhone())
                .currentStage(submitFlag ? RiskReportStage.SUPERVISION.getName() : RiskReportStage.DRAFT.getName())
                .currentStatus(submitFlag ? RiskReportProcessStatus.TO_HANDLE.getName() : RiskReportProcessStatus.DRAFT.getName())
                .createDate(now)
                .createUserId(userModel.getUserId())
                .createUserName(userModel.getRealName())
                .build();
        riskReport.setSequenceNbr(ObjectUtils.isEmpty(dto.getSequenceNbr()) ? sequence.nextId() : dto.getSequenceNbr());
        riskReport.setIsDelete(Boolean.FALSE);
        this.saveOrUpdate(riskReport);
        return riskReport;
    }

    private String buildAllRiskDisposalUnitOrgCode(String defaultOrgCode, Long sequenceNbr) {
        if (!ObjectUtils.isEmpty(sequenceNbr)) {
            String riskDisposalUnitOrgCode = riskReportMapper.getRiskDisposalUnitOrgCode(sequenceNbr);
            String collect = Arrays.stream(riskDisposalUnitOrgCode.split(","))
                    .filter(item -> !item.equals(defaultOrgCode))
                    .collect(Collectors.joining(","));
            return defaultOrgCode + "," + collect;
        }
        return defaultOrgCode;
    }

    private String getRiskTypeNameByCode(String riskTypeCode) {
        List<DictionarieValueModel> riskType = Systemctl.dictionarieClient.dictValues("RISK_TYPE").getResult();
        return riskType.stream()
                .filter(item -> item.getDictDataKey().equals(riskTypeCode))
                .findFirst()
                .map(DictionarieValueModel::getDictDataValue)
                .orElse(null);
    }

    private Map<String, String> getCompanyBySeq(String companySeq) {
        return riskReportMapper.getCompanyBySeq(companySeq);
    }

    public String generateReportNo() {
        String prefix = SEQUENCE_KEY + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        RLock lock = redissonClient.getLock(LOCK_KEY);

        try {
            if (lock.tryLock(10, TimeUnit.SECONDS)) {
                ValueOperations<String, String> ops = redisTemplate.opsForValue();

                String seqStr = ops.get(SEQUENCE_KEY);
                long seq = (seqStr != null) ? Long.parseLong(seqStr) : 0L;
                seq++;

                ops.set(SEQUENCE_KEY, String.valueOf(seq));
                // 设置当天23:59:59过期
                long expireMillis = calculateEndOfDay(new Date()).getTime() - System.currentTimeMillis();
                redisTemplate.expire(SEQUENCE_KEY, expireMillis, TimeUnit.MILLISECONDS);

                String reportNo = prefix + String.format(SEQUENCE_TYPE_AF, seq);
                log.info("生成报表编号：{}", reportNo);
                return reportNo;
            } else {
                throw new RuntimeException("获取锁失败，无法生成报表编号");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程中断异常", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("释放锁");
            }
        }
    }

    private static Date calculateEndOfDay(Date date) {
        LocalDateTime endOfDay = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
                .with(LocalTime.MAX);
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }

    @Override
    public RiskReportDto selectRiskDetail(Long sequenceNbr) {
        RiskReport riskReport = this.baseMapper.selectOne(new LambdaQueryWrapper<RiskReport>()
                .select(RiskReport::getSequenceNbr,
                        RiskReport::getReportNo,
                        RiskReport::getRiskTypeCode,
                        RiskReport::getRiskTypeName,
                        RiskReport::getReportCompanySeq,
                        RiskReport::getReportCompanyName,
                        RiskReport::getRiskCompanySeq,
                        RiskReport::getRiskCompanyName,
                        RiskReport::getRiskDisposalUnitSeq,
                        RiskReport::getRiskDisposalUnitName,
                        RiskReport::getRiskDisposalUnitOrgCode,
                        RiskReport::getAllRiskDisposalUnitOrgCode,
                        RiskReport::getRiskEquipRecord,
                        RiskReport::getOperationUserId,
                        RiskReport::getRiskMainContent,
                        RiskReport::getRiskMainTitle,
                        RiskReport::getRiskMainFile,
                        RiskReport::getReportUserId,
                        RiskReport::getReportUserName,
                        RiskReport::getCurrentStatus,
                        RiskReport::getEndDate,
                        RiskReport::getCreateDate,
                        RiskReport::getCreateUserId,
                        RiskReport::getCreateUserName
                )
                .eq(RiskReport::getSequenceNbr, sequenceNbr));
        List<RiskProcesses> riskProcesses = riskProcessesService.getBaseMapper().selectList(new LambdaQueryWrapper<RiskProcesses>()
                // 排除转办的记录
                // .ne(RiskProcesses::getDisposalWayCode, DisposalMethodEnums.REPORT_FOR_WARDE.getCode())
                .eq(RiskProcesses::getRiskReportSeq, sequenceNbr)
                .eq(RiskProcesses::getIsDelete, Boolean.FALSE)
                .orderByAsc(RiskProcesses::getCreateDate));
        RiskReportDto riskReportDto = new RiskReportDto();
        BeanUtils.copyProperties(riskReport, riskReportDto);
        riskReportDto.setRiskProcesses(riskProcesses);
        riskReportDto.setRiskCompany(riskReport.getRiskCompanySeq() + "_" + riskReport.getRiskCompanyName());
        riskReportDto.setRiskDisposalUnit(riskReport.getRiskDisposalUnitSeq() + "_" + riskReport.getRiskDisposalUnitName());
        return riskReportDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean forWardeBySeq(Long riskReportSeq, RiskProcessesDto riskProcessesDto) {
        ReginParams selectedOrgInfo = getSelectedOrgInfo();
        AgencyUserModel userModel = selectedOrgInfo.getUserModel();
        CompanyBo company = getSelectedOrgInfo().getCompany();
        String riskDisposalUnitSeq = riskProcessesDto.getRiskDisposalUnit().split("_")[0];
        Map<String, String> riskDisposalUnit = riskReportMapper.getCompanyBySeq(riskDisposalUnitSeq);
        String riskDisposalUnitOrgCode = riskDisposalUnit.get(COMPANY_ORG_CODE);
        String riskDisposalUnitCode = riskDisposalUnit.get(COMPANY_CODE);
        String riskDisposalUnitName = riskDisposalUnit.get(COMPANY_NAME);
        String allRiskDisposalUnitOrgCode = buildAllRiskDisposalUnitOrgCode(riskDisposalUnitOrgCode, riskReportSeq);
        this.update(new LambdaUpdateWrapper<RiskReport>().eq(RiskReport::getSequenceNbr, riskReportSeq)
                .set(RiskReport::getRiskDisposalUnitSeq, riskDisposalUnitSeq)
                .set(RiskReport::getRiskDisposalUnitName, riskDisposalUnitName)
                .set(RiskReport::getRiskDisposalUnitCode, riskDisposalUnitCode)
                .set(RiskReport::getAllRiskDisposalUnitOrgCode, allRiskDisposalUnitOrgCode));

        RiskProcesses processes = RiskProcesses.builder()
                .riskReportSeq(riskReportSeq.toString())
                .unitType(RiskUnitType.DISPOSAL.getLabel())
                .disposalWayCode(DisposalMethodEnums.REPORT_FOR_WARDE.getCode())
                .disposalWayName(DisposalMethodEnums.REPORT_FOR_WARDE.getName())
                .disposalContent(riskProcessesDto.getDisposalContent())
                .operationCompanySeq(company.getSequenceNbr().toString())
                .operationCompanyName(company.getCompanyName())
                .operationUserId(userModel.getUserId())
                .operationUserName(userModel.getUserName())
                .createDate(new Date())
                .createUserId(userModel.getUserId())
                .createUserName(userModel.getRealName())
                .build();

        return riskProcessesService.save(processes);
    }

    /**
     * 风险详情中的按钮权限（保存/保存提交/转办/处置/提交整改）根据业务状态走
     */
    @Override
    public List<String> permissionButton(Long sequenceNbr) {
        CompanyBo company = getSelectedOrgInfo().getCompany();
        RiskReport riskReport = this.getById(sequenceNbr);
        String currentStatus = riskReport.getCurrentStatus();
        String riskDisposalUnitCode = riskReport.getRiskDisposalUnitCode();
        String riskCompanyCode = riskReport.getRiskCompanyCode();
        // 草稿
        if (currentStatus.equals(RiskReportProcessStatus.DRAFT.getName())) {
            return Arrays.asList("存草稿", "提交风险");
        }
        // 待处理 + 当前单位是处置单位
        if (currentStatus.equals(RiskReportProcessStatus.TO_HANDLE.getName()) && company.getCompanyCode().equals(riskDisposalUnitCode)) {
            return Arrays.asList("转办", "处置");
        }
        // 待整改 + 当前单位是风险单位
        if (currentStatus.equals(RiskReportProcessStatus.TO_RECTIFY.getName()) && company.getCompanyCode().equals(riskCompanyCode)) {
            return Collections.singletonList("提交整改");
        }
        // 待确认 + 当前单位是处置单位
        if (currentStatus.equals(RiskReportProcessStatus.TO_CONFIRM.getName()) && company.getCompanyCode().equals(riskDisposalUnitCode)) {
            return Arrays.asList("转办", "处置");
        }
        // 已办理 or 其他情况
        return Collections.emptyList();
    }
}