package com.docus.server.report.service.impl;

import com.docus.core.util.Func;
import com.docus.infrastructure.redis.service.IdService;
import com.docus.server.collection.mapper.TBasicMapper;
import com.docus.server.report.api.dto.ReportDto;
import com.docus.server.report.entity.AfCollectTask;
import com.docus.server.report.entity.AfReportRecord;
import com.docus.server.report.entity.AfReportRecordBuffer;
import com.docus.server.report.event.TaskConsumptionReportDownEvent;
import com.docus.server.report.event.ThreePartyPushReportDownEvent;
import com.docus.server.report.mapper.AfCollectTaskMapper;
import com.docus.server.report.mapper.AfReportRecordMapper;
import com.docus.server.report.service.ReportService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 报告服务实现类
 *
 * @author wyb
 */
@Slf4j
@Service
public class ReportServiceImpl implements ReportService {
    private static final Lock REPORT_BUFFER_LOCK = new ReentrantLock();
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private AfCollectTaskMapper collectTaskMapper;
    @Resource
    private AfReportRecordMapper afReportRecordMapper;
    @Resource
    private TBasicMapper tBasicMapper;
    @Resource
    private IdService idService;

    @Override
    public void report(ReportDto reportDto) {
        // 系统无视图生成的任务，消费查询的报告
        if (reportDto.getTaskId() != null) {
            taskConsumptionReport(reportDto);
            return;
        }
        // 第三方推送的报告，任务后生成的
        threePartyPushReports(reportDto);
    }

    @Override
    public void reportBuffer(ReportDto reportDto) {
        // 根据地址判断推送的是文件信息还是地址
        String downUrl = reportDto.getDownUrl();
        boolean isSaveOrUpdateDownloadUrl = Func.isNotBlank(downUrl);
        // 当前一个服务，以后有需要可以用分布式锁
        REPORT_BUFFER_LOCK.lock();
        try {
            AfReportRecordBuffer bufferInfo = afReportRecordMapper.getReportRecordBufferInfo(reportDto.getReportSn());
            if (bufferInfo == null) {
                bufferInfo = new AfReportRecordBuffer(reportDto);
                bufferInfo.setId(idService.getDateSeq());
                // 保存文件地址或者文件信息
                if (isSaveOrUpdateDownloadUrl) {
                    bufferInfo.setFileUrl(reportDto);
                    afReportRecordMapper.saveRecordBufferFileUrl(bufferInfo);
                } else {
                    bufferInfo.setFileInfo(reportDto);
                    afReportRecordMapper.saveRecordBufferFileInfo(bufferInfo);
                }
                return;
            }
            String bufferDownUrl = bufferInfo.getDownUrl();
            String serialnum = bufferInfo.getSerialnum();
           // 设置地址或者文件信息
            if (isSaveOrUpdateDownloadUrl) {
                bufferInfo.setFileUrl(reportDto);
            } else {
                bufferInfo.setFileInfo(reportDto);
            }
            afReportRecordMapper.updateRecordBuffer(bufferInfo);
            // 文件信息和地址都有，可以上报文件
            boolean canReport = (isSaveOrUpdateDownloadUrl && Func.isBlank(bufferDownUrl) && Func.isNotBlank(serialnum)) || (!isSaveOrUpdateDownloadUrl && Func.isNotBlank(bufferDownUrl));
            if (canReport) {
                afReportRecordMapper.cancelBlocking(bufferInfo.getId());
                ReportDto dto = new ReportDto(bufferInfo);
                report(dto);
            }
        } finally {
            REPORT_BUFFER_LOCK.unlock();
        }
    }

    private void taskConsumptionReport(ReportDto reportDto) {
        applicationContext.publishEvent(new TaskConsumptionReportDownEvent(this, reportDto));
        try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    private void threePartyPushReports(ReportDto reportDto) {


        String patientId = null;
        try {
            // 如果出现多条出错的情况，还是得保存收到的信息，人工干预处理
            patientId = tBasicMapper.getPatientIdByInpatientNoAndAdminssTimes(reportDto.getInpatientNo(), reportDto.getAdmisstimes());
        } catch (Exception ex) {
            log.error("查询病案主键出错了，病案号："+reportDto.getInpatientNo()+" ，住院次数："+reportDto.getAdmisstimes(), ex);
        }
        // 不验证数据，始终保存收到的信息
        AfReportRecord afReportRecord = afReportRecordMapper.getRecordBySerialnumAndInpatientNoAndSysFlag(reportDto.getSerialnum(), reportDto.getInpatientNo(), reportDto.getAdmisstimes(), reportDto.getSysFlag());
        if (afReportRecord == null) {
            long id = idService.getDateSeq();
            afReportRecord = new AfReportRecord(reportDto);
            afReportRecord.setTaskId(id);
            afReportRecord.setPatientId(patientId);
            afReportRecordMapper.saveRecord(afReportRecord);
        } else {
            // 更新 主要更新 url
            afReportRecord.setDownUrl(reportDto.getDownUrl());
            afReportRecord.setDownType(reportDto.getDowntype());
            afReportRecord.setFileName(reportDto.getFileTitle());
            afReportRecord.setPatientId(patientId);
            afReportRecord.setZdAssortId(reportDto.getAssortId());
            afReportRecordMapper.updateRecordByTaskId(afReportRecord);
        }
        // 不使用事务，不需要回滚上面的保存

        // 根据记录中的任务id，查询是否需要新增任务
        if (Func.isBlank(patientId)) {
            log.warn("病案号：{}，住院次数：{} 未找到病案基础数据，暂不进行下载任务！", reportDto.getInpatientNo(), reportDto.getAdmisstimes());
            return;
        }
        // 判断是否需要保存任务
        AfCollectTask afCollectTask = collectTaskMapper.getTaskById(afReportRecord.getTaskId());
        if (afCollectTask == null) {
            afCollectTask = new AfCollectTask();
            afCollectTask.setId(afReportRecord.getTaskId());
            afCollectTask.setC1(reportDto.getSerialnum());
            afCollectTask.setC2(reportDto.getFileTitle());
            afCollectTask.setC3(reportDto.getJzh());
            afCollectTask.setPatientId(patientId);
            afCollectTask.setSysflag(reportDto.getSysFlag());
            afCollectTask.setState("0");
            afCollectTask.setPatientId(patientId);
            collectTaskMapper.saveTask(afCollectTask);
        }
        // 都成功后发布下载事件
        applicationContext.publishEvent(new ThreePartyPushReportDownEvent(this, afReportRecord.getTaskId()));
        try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    @Override
    public void makeupThreePartyPushReportByTaskIds(List<Long> taskIds) throws Exception {
        if (taskIds == null || taskIds.isEmpty()) {
            return;
        }
        // 定义一批200查询，分批次
        final int oneBatchCount = 200;
        int startIndex = 0;
        int toIndex = oneBatchCount;
        boolean loop = true;
        do {
            // 获取截取下标
            if (taskIds.size() <= oneBatchCount) {
                toIndex = taskIds.size();
                // 最后一批截取
                loop = false;
            }
            // 截取每一批
            List<Long> makeupTaskIdsSub = taskIds.subList(startIndex, toIndex);
            List<Long> makeupTaskIds = afReportRecordMapper.getHasPatientIdTaskIdsByTaskIds(makeupTaskIdsSub);
            makeupTaskIdsSub.clear();
            if (Func.isNotEmpty(makeupTaskIds)) {
                for (Long taskId : makeupTaskIds) {
                    // 发布下载事件
                    applicationContext.publishEvent(new ThreePartyPushReportDownEvent(this, taskId));
                    // 等待防止过快
                    TimeUnit.MILLISECONDS.sleep(50);
                }
            }
            // 当截取长度小于集合长度，可以进行下次循环截取
        } while (loop);
    }
}
