package com.hotwj.platform.securityManagement.violationInfo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.common.helper.DriverLoginContextHelper;
import com.hotwj.platform.flow.domain.SysFlowInstance;
import com.hotwj.platform.flow.domain.SysFlowTask;
import com.hotwj.platform.flow.domain.SysFlowTaskHis;
import com.hotwj.platform.flow.mapper.SysFlowInstanceMapper;
import com.hotwj.platform.flow.mapper.SysFlowTaskHisMapper;
import com.hotwj.platform.flow.service.ISysFlowService;
import com.hotwj.platform.integration.ocr.SignatureVerifyService;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo;
import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo;
import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo;
import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper;
import com.hotwj.platform.securityManagement.violationInfo.service.IHotViolationInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
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.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 违规信息Service业务层处理
 *
 * @author shihongwei
 * @date 2026-01-06
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HotViolationInfoServiceImpl implements IHotViolationInfoService {

    private static final String FLOW_CODE_VIOLATION_HANDLE = "VIOLATION_HANDLE";
    private static final String NODE_PARTY_SIGN = "PARTY_SIGN_NODE";
    private static final String NODE_WAIT_INTERVIEW_INFO = "WAIT_INTERVIEW_INFO_NODE";
    private static final String NODE_INTERVIEWED_SIGN = "INTERVIEW_SIGN_NODE";
    private static final String NODE_AUTO_DONE = "AUTO_DONE_NODE";
    private static final long PROCESS_STATUS_TODO = 0L;
    private static final long PROCESS_STATUS_DONE = 2L;

    private final HotViolationInfoMapper baseMapper;
    private final IHotSystemNotificationService notificationService;
    private final ISysFlowService flowService;
    private final SysFlowInstanceMapper instanceMapper;
    private final SysFlowTaskHisMapper taskHisMapper;
    private final SignatureVerifyService signatureVerifyService;

    private static boolean isNeedInterview(Long needInterview) {
        return Long.valueOf(1L).equals(needInterview);
    }

    private static boolean isInterviewInfoComplete(HotViolationInfo info) {
        return info != null
                && info.getInterviewTime() != null
                && StringUtils.isNotBlank(info.getInterviewPlace())
                && StringUtils.isNotBlank(info.getInterviewReason())
                && StringUtils.isNotBlank(info.getInterviewContent())
                && StringUtils.isNotBlank(info.getInterviewerSignatureImage());
    }

    /**
     * 处理违规信息流程
     */
    private void reconcileViolationFlow(Long violationId) {
        if (violationId == null) {
            return;
        }
        HotViolationInfo info = baseMapper.selectById(violationId);
        if (info == null || info.getCompanyId() == null || StringUtils.isBlank(info.getDriverId())) {
            return;
        }
        String businessId = String.valueOf(violationId);
        if (info.getProcessStatus() != null
                && info.getProcessStatus() == PROCESS_STATUS_DONE
                && hasViolationDoneHistory(info.getDriverId(), businessId)) {
            return;
        }

        long runningCount = instanceMapper.selectCount(
                Wrappers.<SysFlowInstance>lambdaQuery()
                        .eq(SysFlowInstance::getFlowCode, FLOW_CODE_VIOLATION_HANDLE)
                        .eq(SysFlowInstance::getBusinessId, businessId)
                        .eq(SysFlowInstance::getStatus, 0)
        );
        if (runningCount == 0) {
            LoginUser loginUser = LoginHelper.getLoginUser();
            flowService.startFlow(FLOW_CODE_VIOLATION_HANDLE, businessId, info.getCompanyId(), loginUser.getBusinessUserId(), info.getDriverId());
        }
        autoAdvanceViolationFlow(violationId);
    }

    /**
     * 自动推进违规处理流程
     */
    private void autoAdvanceViolationFlow(Long violationId) {
        if (violationId == null) {
            return;
        }
        for (int i = 0; i < 6; i++) {
            HotViolationInfo info = baseMapper.selectById(violationId);
            if (info == null || StringUtils.isBlank(info.getDriverId()) || info.getCompanyId() == null) {
                return;
            }
            String businessId = String.valueOf(violationId);

            String auditorId = info.getDriverId();

            String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, businessId, auditorId);

            if (StringUtils.isBlank(taskId)) {
                auditorId = "SYSTEM";
                taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, businessId, auditorId);
            }
            if (StringUtils.isBlank(taskId)) {
                return;
            }
            SysFlowTask task = flowService.getTaskById(taskId);
            if (task == null) {
                return;
            }
            String nodeCode = task.getNodeCode();

            boolean shouldAudit = false;
            if (NODE_PARTY_SIGN.equals(nodeCode)) {
                shouldAudit = StringUtils.isNotBlank(info.getPartySignatureImage());
            } else if (NODE_INTERVIEWED_SIGN.equals(nodeCode)) {
                shouldAudit = StringUtils.isNotBlank(info.getInterviewedSignatureImage());
            } else if (NODE_WAIT_INTERVIEW_INFO.equals(nodeCode)) {
                shouldAudit = isInterviewInfoComplete(info);
                auditorId = "SYSTEM";
            } else if (NODE_AUTO_DONE.equals(nodeCode)) {
                shouldAudit = true;
            }

            if (!shouldAudit) {
                return;
            }
            flowService.audit(taskId, true, "自动流转", auditorId);
        }
    }

    private void validateAuditForNode(SysFlowTask task, HotViolationInfo info, String interviewedSignatureImage, String partySignatureImage) {
        if (task == null || info == null) {
            throw new ServiceException("业务数据异常");
        }
        String nodeCode = task.getNodeCode();
        if (NODE_PARTY_SIGN.equals(nodeCode)) {
            if (StringUtils.isBlank(partySignatureImage) && StringUtils.isBlank(info.getPartySignatureImage())) {
                throw new ServiceException("请提供当事人签名");
            }
            validateMobileSignatureIfNeeded(info.getDriverName(), partySignatureImage);
            return;
        }
        if (NODE_INTERVIEWED_SIGN.equals(nodeCode)) {
            if (!isNeedInterview(info.getNeedInterview())) {
                throw new ServiceException("当前违规不需要约谈");
            }
            if (!isInterviewInfoComplete(info)) {
                throw new ServiceException("约谈信息未填写，无法签字提交");
            }
            if (StringUtils.isBlank(info.getPartySignatureImage()) && StringUtils.isBlank(partySignatureImage)) {
                throw new ServiceException("请先完成当事人签名");
            }
            if (StringUtils.isBlank(interviewedSignatureImage) && StringUtils.isBlank(info.getInterviewedSignatureImage())) {
                throw new ServiceException("请提供被约谈人签名");
            }
            validateMobileSignatureIfNeeded(info.getDriverName(), interviewedSignatureImage);
            return;
        }

        // TODO 移动端验证签名
        throw new ServiceException("当前节点不支持签字提交");
    }

    private void validateMobileSignatureIfNeeded(String userName, String signatureImage) {
        if (!DriverLoginContextHelper.isDriverPort() || StringUtils.isBlank(signatureImage)) {
            return;
        }
        if (StringUtils.isBlank(userName)) {
            throw new ServiceException("签署人姓名不能为空");
        }
        String first = signatureImage.split(",")[0].trim();
        if (!first.matches("^\\d+$")) {
            throw new ServiceException("签名文件格式错误");
        }
        signatureVerifyService.validateSelfSignature(Long.parseLong(first), userName);
    }

    /**
     * 查询违规信息
     *
     * @param id 主键
     * @return 违规信息
     */
    @Override
    public HotViolationInfoVo queryById(Long id) {
        HotViolationInfoVo vo = baseMapper.selectVoById(id);
        if (vo != null) {
            // 填充待办taskId，方便前端详情页直接办理
            // 这里查询该违规记录对应驾驶员的待办
            String driverId = vo.getDriverId();
            if (StringUtils.isNotBlank(driverId)) {
                // 只有未完成的才可能有待办
                if (vo.getProcessStatus() != null) {
                    String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, String.valueOf(vo.getId()), driverId);
                    vo.setTaskId(taskId);
                }
            }
        }
        return vo;
    }

    /**
     * 分页查询违规信息列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 违规信息分页列表
     */
    @Override
    public TableDataInfo<HotViolationInfoVo> queryPageList(HotViolationInfoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotViolationInfo> lqw = buildQueryWrapper(bo);
        Page<HotViolationInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        // 填充taskId
        fillTaskId(result.getRecords(), null);

        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的违规信息列表
     *
     * @param bo 查询条件
     * @return 违规信息列表
     */
    @Override
    public List<HotViolationInfoVo> queryList(HotViolationInfoBo bo) {
        LambdaQueryWrapper<HotViolationInfo> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public TableDataInfo<HotViolationInfoVo> queryDriverList(HotViolationInfoBo bo, PageQuery pageQuery) {
        // 强制设置 driverId 为当前登录人，防止查询他人数据
        LoginUser loginUser = LoginHelper.getLoginUser();
        String driverId;
        if (loginUser != null) {
            driverId = String.valueOf(loginUser.getBusinessUserId());
            bo.setDriverId(driverId);
        } else {
            throw new ServiceException("未登录或无驾驶员身份");
        }
        Page<HotViolationInfoVo> result = baseMapper.queryDriverViolationList(pageQuery.build(), bo);

        // 填充taskId，只查询当前驾驶员的待办
        fillTaskId(result.getRecords(), driverId);

        return TableDataInfo.build(result);
    }

    /**
     * 填充待办任务ID
     *
     * @param list       违规列表
     * @param approverId 审批人ID（如果为null则不限制审批人，通常用于管理员查看）
     */
    private void fillTaskId(List<HotViolationInfoVo> list, String approverId) {
        if (list == null || list.isEmpty()) {
            return;
        }

        LoginUser loginUser = LoginHelper.getLoginUser();
        String currentUserId = (approverId != null) ? approverId : (loginUser != null ? String.valueOf(loginUser.getBusinessUserId()) : null);

        if (currentUserId == null) {
            return;
        }

        for (HotViolationInfoVo vo : list) {
            // 只有未完成的才可能有待办
            if (vo.getProcessStatus() != null) {
                String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, String.valueOf(vo.getId()), currentUserId);
                vo.setTaskId(taskId);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auditAndComplete(String taskId, Long processStatus, String disposalResult, String interviewedSignatureImage, String partySignatureImage) {
        SysFlowTask task = flowService.getTaskById(taskId);
        if (task == null) {
            throw new ServiceException("任务不存在");
        }
        SysFlowInstance instance = flowService.getInstanceByTaskId(taskId);
        if (instance == null) {
            throw new ServiceException("流程实例不存在");
        }
        Long violationId;
        try {
            violationId = Long.valueOf(instance.getBusinessId());
        } catch (NumberFormatException e) {
            throw new ServiceException("业务数据异常");
        }
        HotViolationInfo info = baseMapper.selectById(violationId);
        if (info == null) {
            throw new ServiceException("违规信息不存在");
        }

        validateAuditForNode(task, info, interviewedSignatureImage, partySignatureImage);

        if (StringUtils.isNotBlank(disposalResult)) {
            info.setDisposalResult(disposalResult);
        }
        if (StringUtils.isNotBlank(interviewedSignatureImage)) {
            info.setInterviewedSignatureImage(interviewedSignatureImage);
        }
        if (StringUtils.isNotBlank(partySignatureImage)) {
            info.setPartySignatureImage(partySignatureImage);
        }
        baseMapper.updateById(info);

        LoginUser loginUser = LoginHelper.getLoginUser();
        String approverId = task.getApproverId();
        if (loginUser == null || !StringUtils.equals(loginUser.getBusinessUserId(), approverId)) {
            throw new ServiceException("您不是当前节点的审批人");
        }
        flowService.audit(taskId, true, "违规处理完成", approverId);
        autoAdvanceViolationFlow(violationId);

        return true;
    }

    private LambdaQueryWrapper<HotViolationInfo> buildQueryWrapper(HotViolationInfoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotViolationInfo> lqw = Wrappers.lambdaQuery();
        lqw.orderByDesc(HotViolationInfo::getOccurTime);
        if (bo.getOccurTime() != null) {
            java.util.Calendar calendarStart = java.util.Calendar.getInstance();
            calendarStart.setTime(bo.getOccurTime());
            calendarStart.set(java.util.Calendar.HOUR_OF_DAY, 0);
            calendarStart.set(java.util.Calendar.MINUTE, 0);
            calendarStart.set(java.util.Calendar.SECOND, 0);
            calendarStart.set(java.util.Calendar.MILLISECOND, 0);
            java.util.Calendar calendarEnd = (java.util.Calendar) calendarStart.clone();
            calendarEnd.add(java.util.Calendar.DAY_OF_MONTH, 1);
            lqw.ge(HotViolationInfo::getOccurTime, calendarStart.getTime());
            lqw.lt(HotViolationInfo::getOccurTime, calendarEnd.getTime());
        }
        lqw.eq(bo.getCompanyId() != null, HotViolationInfo::getCompanyId, bo.getCompanyId());
        lqw.eq(bo.getVehicleId() != null, HotViolationInfo::getVehicleId, bo.getVehicleId());
        lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotViolationInfo::getPlateNumber, bo.getPlateNumber());
        lqw.eq(bo.getDriverId() != null, HotViolationInfo::getDriverId, bo.getDriverId());
        lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotViolationInfo::getDriverName, bo.getDriverName());
        lqw.eq(bo.getViolationType() != null, HotViolationInfo::getViolationType, bo.getViolationType());
        lqw.eq(bo.getBillingDepartment() != null, HotViolationInfo::getBillingDepartment, bo.getBillingDepartment());
        lqw.eq(bo.getIsDrunkDriving() != null, HotViolationInfo::getIsDrunkDriving, bo.getIsDrunkDriving());
        lqw.eq(StringUtils.isNotBlank(bo.getViolationNo()), HotViolationInfo::getViolationNo, bo.getViolationNo());
        lqw.eq(bo.getFineAmount() != null, HotViolationInfo::getFineAmount, bo.getFineAmount());
        lqw.eq(bo.getDeductPoints() != null, HotViolationInfo::getDeductPoints, bo.getDeductPoints());
        lqw.eq(bo.getDeductionType() != null, HotViolationInfo::getDeductionType, bo.getDeductionType());
        lqw.eq(bo.getRecorderId() != null, HotViolationInfo::getRecorderId, bo.getRecorderId());
        lqw.like(StringUtils.isNotBlank(bo.getRecorderName()), HotViolationInfo::getRecorderName, bo.getRecorderName());
        lqw.eq(bo.getNeedInterview() != null, HotViolationInfo::getNeedInterview, bo.getNeedInterview());
        lqw.eq(StringUtils.isNotBlank(bo.getViolationPlace()), HotViolationInfo::getViolationPlace, bo.getViolationPlace());
        lqw.eq(StringUtils.isNotBlank(bo.getViolationContent()), HotViolationInfo::getViolationContent, bo.getViolationContent());
        lqw.eq(StringUtils.isNotBlank(bo.getDisposalResult()), HotViolationInfo::getDisposalResult, bo.getDisposalResult());
        lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrls()), HotViolationInfo::getAttachmentUrls, bo.getAttachmentUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getResponsibleSignatureImage()), HotViolationInfo::getResponsibleSignatureImage, bo.getResponsibleSignatureImage());
        lqw.eq(StringUtils.isNotBlank(bo.getPartySignatureImage()), HotViolationInfo::getPartySignatureImage, bo.getPartySignatureImage());
        lqw.eq(bo.getInterviewTime() != null, HotViolationInfo::getInterviewTime, bo.getInterviewTime());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewPlace()), HotViolationInfo::getInterviewPlace, bo.getInterviewPlace());
        lqw.eq(bo.getInterviewRecorderId() != null, HotViolationInfo::getInterviewRecorderId, bo.getInterviewRecorderId());
        lqw.like(StringUtils.isNotBlank(bo.getInterviewRecorderName()), HotViolationInfo::getInterviewRecorderName, bo.getInterviewRecorderName());
        lqw.eq(bo.getInterviewPersonId() != null, HotViolationInfo::getInterviewPersonId, bo.getInterviewPersonId());
        lqw.like(StringUtils.isNotBlank(bo.getInterviewPersonName()), HotViolationInfo::getInterviewPersonName, bo.getInterviewPersonName());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewReason()), HotViolationInfo::getInterviewReason, bo.getInterviewReason());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewContent()), HotViolationInfo::getInterviewContent, bo.getInterviewContent());
        lqw.eq(StringUtils.isNotBlank(bo.getPromiseUrls()), HotViolationInfo::getPromiseUrls, bo.getPromiseUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewPhotoUrls()), HotViolationInfo::getInterviewPhotoUrls, bo.getInterviewPhotoUrls());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewerSignatureImage()), HotViolationInfo::getInterviewerSignatureImage, bo.getInterviewerSignatureImage());
        lqw.eq(StringUtils.isNotBlank(bo.getInterviewedSignatureImage()), HotViolationInfo::getInterviewedSignatureImage, bo.getInterviewedSignatureImage());
        lqw.eq(bo.getProcessStatus() != null, HotViolationInfo::getProcessStatus, bo.getProcessStatus());
        lqw.eq(bo.getIsDeleted() != null, HotViolationInfo::getIsDeleted, bo.getIsDeleted());
        return lqw;
    }

    /**
     * 新增违规信息
     *
     * @param bo 违规信息
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(HotViolationInfoBo bo) {
        HotViolationInfo add = MapstructUtils.convert(bo, HotViolationInfo.class);
        if (add.getProcessStatus() == null) {
            add.setProcessStatus(PROCESS_STATUS_TODO);
        }
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            reconcileViolationFlow(add.getId());
            if (StringUtils.isNotBlank(add.getDriverId())) {
                sendViolationNotification(add.getDriverId(), "您的违规信息已记录，请及时处理。", add.getId());
            }
        }
        return flag;
    }

    /**
     * 修改违规信息
     *
     * @param bo 违规信息
     * @return 是否修改成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(HotViolationInfoBo bo) {
        HotViolationInfo before = null;
        if (bo.getId() != null) {
            before = baseMapper.selectById(bo.getId());
        }
        HotViolationInfo update = MapstructUtils.convert(bo, HotViolationInfo.class);
        validEntityBeforeSave(update);
        boolean ok = baseMapper.updateById(update) > 0;
        if (ok) {
            reconcileViolationFlow(update.getId());
            String driverId = update.getDriverId();
            if (StringUtils.isBlank(driverId) && before != null) {
                driverId = before.getDriverId();
            }
            if (StringUtils.isNotBlank(driverId) && update.getId() != null) {
                sendViolationNotification(driverId, "您的违规信息已更新，请及时处理。", update.getId());
            }
        }
        return ok;
    }

    private boolean hasViolationDoneHistory(String driverId, String businessId) {
        if (StringUtils.isBlank(driverId) || StringUtils.isBlank(businessId)) {
            return false;
        }
        List<SysFlowInstance> instances = instanceMapper.selectList(
                Wrappers.<SysFlowInstance>lambdaQuery()
                        .eq(SysFlowInstance::getFlowCode, FLOW_CODE_VIOLATION_HANDLE)
                        .eq(SysFlowInstance::getBusinessId, businessId)
        );
        if (instances == null || instances.isEmpty()) {
            return false;
        }
        List<String> instanceIds = instances.stream().map(SysFlowInstance::getInstanceId).toList();
        return taskHisMapper.selectCount(
                Wrappers.<SysFlowTaskHis>lambdaQuery()
                        .in(SysFlowTaskHis::getInstanceId, instanceIds)
                        .eq(SysFlowTaskHis::getApproverId, driverId)
        ) > 0;
    }

    /**
     * 发送违规系统通知
     */
    private void sendViolationNotification(String driverId, String content, Long violationId) {
        if (StringUtils.isBlank(driverId)) {
            return;
        }
        HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
        bos.setLevel("普通");
        bos.setContent(content);
        bos.setSourceType("违规信息");
        bos.setSenderType("SYSTEM");
        bos.setReceiverType("驾驶员");
        bos.setReceiverIds(java.util.Collections.singletonList(driverId));
        bos.setIsDeleted(0L);
        try {
            notificationService.insertByBo(bos);
        } catch (Exception e) {
            log.warn("发送违规信息通知失败 driverId={} violationId={}", driverId, violationId, e);
        }
    }

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

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