package thyyxxk.webserver.service.outpatient.triage;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import thyyxxk.webserver.config.exception.BizException;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.constants.Capacity;
import thyyxxk.webserver.constants.Gender;
import thyyxxk.webserver.dao.his.outpatient.triage.EmergencyRegDao;
import thyyxxk.webserver.dao.his.outpatient.triage.TriageDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.dictionary.CodeName;
import thyyxxk.webserver.entity.outpatient.triage.*;
import thyyxxk.webserver.http.websocket.SocketV2;
import thyyxxk.webserver.http.websocket.dto.WebSocketByListUserCode;
import thyyxxk.webserver.service.hutoolcache.UserCache;
import thyyxxk.webserver.service.outpatient.wxapi.SendWxInfoService;
import thyyxxk.webserver.utils.AssertUtil;
import thyyxxk.webserver.utils.DateUtil;
import thyyxxk.webserver.utils.IdCardUtil;
import thyyxxk.webserver.utils.ResultVoUtil;
import thyyxxk.webserver.utils.StringUtil;
import thyyxxk.webserver.utils.TokenUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author dj
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TriageService {
    private final TriageDao dao;
    private final EmergencyRegDao erdao;
    private final SendWxInfoService sendWxInfoService;
    private final UserCache userCache;
    private final SocketV2 socketV2;

    public String getBigScreenFooter() {
        return dao.getBigScreenFooter();
    }

    public ResultVo<Map<String, Object>> getUnTriagedPatients() {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        Map<String, Object> map = new HashMap<>(Capacity.TWO);
        List<MzfzPatientOrder> list = dao.getUnTriagedPatients(depts);
        fillGenderAndAge(list);
        map.put("totalSize", list.size());
        map.put("list", list);
        return ResultVoUtil.success(map);
    }

    private void fillGenderAndAge(List<MzfzPatientOrder> list) {
        for (MzfzPatientOrder item : list) {
            if (IdCardUtil.isValidatedIdCard(item.getSocialNo())) {
                Gender gender = IdCardUtil.getGenderByIdCard(item.getSocialNo());
                item.setGender(gender == Gender.MALE ? "男" : "女");
                Integer age = IdCardUtil.getAgeByIdCard(item.getSocialNo());
                if (null == age) {
                    item.setAge("未知");
                } else {
                    item.setAge(age + "岁");
                }
            } else {
                item.setGender("未知");
                item.setAge("未知");
            }
        }
    }

    public ResultVo<List<String>> getPatientIdByIdNo(String idNo) {
        List<String> ids = dao.selectPatientIdByIdNo(idNo);
        if (ids.isEmpty()) {
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "患者在我院的就诊卡没有绑定身份证，无法查找。");
        }
        return ResultVoUtil.success(ids);
    }

    public ResultVo<List<MzfzPatientOrder>> getTriagedPatients() {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        List<MzfzPatientOrder> list = dao.getTriagedPatients(depts);
        fillGenderAndAge(list);
        return ResultVoUtil.success(list);
    }

    public ResultVo<List<MzfzPatientOrder>> getTreatedPatients() {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        List<MzfzPatientOrder> list = dao.getTreatedPatients(depts);
        fillGenderAndAge(list);
        return ResultVoUtil.success(list);
    }

    public ResultVo<List<FloorScreen>> getBigScreenData() {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        List<MzfzPatientOrder> list = dao.getBigScreenData(depts);
        Map<String, FloorScreen> map = new HashMap<>(Capacity.DEFAULT);
        for (MzfzPatientOrder item : list) {
            item.setName(StringUtil.desensitizeName(item.getName()));
            String key = String.format("%s_%s", item.getRoomCode(), item.getDoctorCode());
            if (!map.containsKey(key)) {
                FloorScreen fl = new FloorScreen();
                fl.setDeptName(item.getDeptName());
                fl.setDoctorTitle(item.getDoctorTitle());
                fl.setDoctorName(item.getDoctorName());
                fl.setRoomNo(item.getRoomNo());
                fl.setRoomCode(item.getRoomCode());
                List<MzfzPatientOrder> temp = new ArrayList<>();
                temp.add(item);
                fl.setPatients(temp);
                map.put(key, fl);
            } else {
                map.get(key).getPatients().add(item);
            }
        }
        List<FloorScreen> ret = new ArrayList<>();
        for (Map.Entry<String, FloorScreen> entry : map.entrySet()) {
            ret.add(entry.getValue());
        }
        for (FloorScreen item : ret) {
            int size = item.getPatients().size();
            if (size % 4 == 0) {
                item.setPageNum(size / 4);
            } else {
                item.setPageNum(size / 4 + 1);
            }

        }
        return ResultVoUtil.success(ret);
    }

    public ResultVo<List<CodeName>> getChosenDept() {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        return ResultVoUtil.success(dao.getDept(depts));
    }

    public ResultVo<List<MzfzZdDeptRoom>> getRooms(String deptCode) {
        return ResultVoUtil.success(dao.getRooms(deptCode));
    }

    public ResultVo<String> hasDoneCovidAssessment(String patientId) {
        final String socialNo = dao.getSocialNo(patientId);
        final Integer has = dao.getCovidAssessment(socialNo);
        if (null == has) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "此患者近期未做流调。");
        }
        return ResultVoUtil.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultVo<String> fenZhen(MzfzZdDeptRoom room) {
        room.setTriageStaff(TokenUtil.getInstance().getTokenUserId());
        int patientNum = dao.getPatientNum(room.getDeptCode(), room.getRoomCode());
        if (patientNum >= 0) {
            room.setPatientNum(patientNum);
        }
        dao.updatePatientNum(room.getDeptCode(), room.getRoomCode(), patientNum);
        dao.fenZhen(room);
        log.info("分诊: update mzfz_patient_order set status_flag=1, slow_flag=0, dept_code={}, room_code={}, " +
                 "doctor_code={}, fz_no={}, fz_flag={} where serial_no={}",
                room.getDeptCode(), room.getRoomCode(), room.getDoctorCode(),
                room.getPatientNum(), room.getFuzhenFlag(), room.getSerialNo());
        dao.deleteNotifyRelation(room.getSerialNo());
        dao.insertNotifyRelation(room.getSerialNo(), room.getTriageStaff(), room.getRoomCode());

        MessageForPush messageForPush = dao.selectMessageForPush(room.getSerialNo());
        messageForPush.setAction(4);
        messageForPush.setBlurName(StringUtil.desensitizeName(messageForPush.getName()));
        messageForPush.setSid(room.getTriageStaff());
        messageForPush.setRoomCode(room.getRoomCode());
        socketV2.sendMsgToHelpDesk(messageForPush);

        return ResultVoUtil.success("分诊成功。");
    }

    public void notifyComplete(Integer serialNo) {
        log.info("通知完毕>>> {}", serialNo);
        dao.updateNotifyDatetime(serialNo);
    }

    public ResultVo<Integer> fuZhen(Integer serialNo) {
        return ResultVoUtil.success(dao.fuZhen(serialNo));
    }

    public ResultVo<Integer> cancelTriage(Integer serialNo) {
        Integer ret = dao.cancelTriage(serialNo);
        dao.deleteNotifyRelation(serialNo);
        MessageForPush messageForPush = new MessageForPush();
        messageForPush.setAction(5);
        messageForPush.setSerialNo(serialNo);
        messageForPush.setSid(TokenUtil.getInstance().getTokenUserId());
        socketV2.sendMsgToHelpDesk(messageForPush);
        return ResultVoUtil.success(ret);
    }

    public String refreshBigScreenData() {
        MessageForPush messageForPush = new MessageForPush();
        messageForPush.setAction(3);
        messageForPush.setSid(TokenUtil.getInstance().getTokenUserId());
        socketV2.sendMsgToHelpDesk(messageForPush);
        return "OK";
    }

    public ResultVo<List<MzfzPatientOrder>> getAllPatients(String searchContent) {
        String[] depts = StringUtil.triageDeptString2Array(dao.selectChosenDepts(TokenUtil.getInstance().getTokenUserId()));
        if (depts.length == 0) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请先前往【科室设置】分配分诊科室！");
        }
        searchContent = StringUtil.isBlank(searchContent) ? "%%" : searchContent + "%";
        List<MzfzPatientOrder> list = dao.getAllPatients(depts, searchContent);
        return ResultVoUtil.success(list);
    }

    public ResultVo<MzYshTzxx> getMzVitalSigns(String patientId, Integer times) {
        log.info("获取生命体征：{}", patientId);
        MzYshTzxx vitalSigns = dao.getMzVitalSigns(patientId, times);
        if (null == vitalSigns) {
            vitalSigns = new MzYshTzxx();
        }
        vitalSigns.setPatientId(patientId);
        vitalSigns.setTimes(times);
        vitalSigns.setVisitDate(new Date());
        return ResultVoUtil.success(vitalSigns);
    }

    public ResultVo<String> saveMzVitalSigns(MzYshTzxx param) {
        log.info("保存生命体征：{}", param);
        MzYshTzxx tzxx =  dao.getMzVitalSigns(param.getPatientId(), param.getTimes());
        if (null == tzxx) {
            dao.insertMzVitalSigns(param);
        } else {
            dao.updateMzVitalSigns(param);
        }
        return ResultVoUtil.success("保存成功");
    }

    public ResultVo<String> notifyMessage(MessageForPush param) {
        if (null != param.getAction() && param.getAction() == 99) {
            return notifyVipReq(param.getSerialNo());
        }
        MessageForPush messageForPush = dao.selectMessageForPush(param.getSerialNo());
        if (null == messageForPush) {
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "没有找到此患者的分诊信息！");
        }
        messageForPush.setAction(param.getAction());
        messageForPush.setBlurName(StringUtil.desensitizeName(messageForPush.getName()));
        TriageNotifyRelation notifyRelation = dao.selectTriageNotifyRelation(messageForPush.getSerialNo());
        if (null == notifyRelation) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有找到对应的分诊关系。");
        }
        messageForPush.setSid(notifyRelation.getSocketSid());
        messageForPush.setRoomCode(notifyRelation.getRoomCode());
        socketV2.sendMsgToHelpDesk(messageForPush);
        dao.updateNotifyDatetime(param.getSerialNo());

        messageForPush.setSid(messageForPush.getRoomCode());
        socketV2.sendMsgToRoomScreen(messageForPush);
        return ResultVoUtil.success("通知完成。");
    }

    private ResultVo<String> notifyVipReq(int serialNo) {
        MessageForPush vipRegMessage = dao.selectVipRegMessage(serialNo);
        String message = String.format("患者【%s】预约了%s，预约就诊时间为【%s】",
                vipRegMessage.getName(), vipRegMessage.getDeptName(),
                DateUtil.formatDatetime(vipRegMessage.getVisitDate()));
        JSONObject messageWrapper = new JSONObject();
        messageWrapper.put("name", "systemNotification");
        JSONObject messageBody = new JSONObject();
        messageBody.put("title", "提醒");
        messageBody.put("message", message);
        messageWrapper.put("message", messageBody);
        List<String> vipTriageUsers = dao.selectVipTriageUser();
        if (!vipTriageUsers.isEmpty()) {
            socketV2.sendUserCodeListMessage(
                    WebSocketByListUserCode.create(vipTriageUsers, messageWrapper.toJSONString())
            );
        }
        List<String> codeRsList = dao.selectCodeRsList();
        if (!codeRsList.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (String codeRs : codeRsList) {
                sb.append("|").append(codeRs);
            }
            String touser = sb.substring(1);
            sendWxInfoService.sendCorpWxMsg(touser, message);
        }
        return ResultVoUtil.success();
    }

    public ResultVo<String> saveNewCard(String oldPatientId, String newPatientId) {
        Map<String, Object> oldPatientInfo = dao.selectCardInfo(oldPatientId);
        AssertUtil.isnotBlank(oldPatientInfo, "未查询到该病人信息,请确认旧门诊号是否填写正确?");
        Map<String, Object> patientInfo = dao.selectCardInfo(newPatientId);
        AssertUtil.isnotBlank(patientInfo, "未查询到该病人信息,请确认新门诊号是否填写正确?");
        if (!Convert.toStr(oldPatientInfo.get("name"), "").equals(Convert.toStr(patientInfo.get("name"), ""))) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, StrUtil.format("旧门诊号对应的病人为[{}],新门诊号对应的病人为[{}],不是同一病人,请重新填写数据!", Convert.toStr(oldPatientInfo.get("name"), ""), Convert.toStr(patientInfo.get("name"), "")));
        }
        dao.saveNewCard(oldPatientId, newPatientId, Convert.toStr(patientInfo.get("ic_card_no"), ""));
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "保存成功");
    }

    public ResultVo<Map<String, Object>> queryCardInfo(String oldPatientId, String newPatientId) {
        AssertUtil.isnotBlank(oldPatientId, "旧门诊号不能为空");
        AssertUtil.isnotBlank(newPatientId, "新门诊号不能为空");
        Map<String, Object> patientInfo = dao.selectCardInfo(newPatientId);
        AssertUtil.isnotBlank(patientInfo, "未查询到该病人信息,请确认新门诊号是否填写正确?");
        return ResultVoUtil.success(patientInfo);
    }

    public EmergencyRegInfo getEmergencyRegInfo(String patientId, Integer times) {
        QueryWrapper<EmergencyRegInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("patient_id", patientId);
        wrapper.eq("times", times);
        EmergencyRegInfo info = erdao.selectOne(wrapper);
        if (null == info) {
            info = erdao.getRawEntity(patientId, times);
        }
        if (null == info) {
            return null;
        }
        String idCard = info.getIdCard();
        if (IdCardUtil.isValidatedIdCard(idCard)) {
            info.setAge(IdCardUtil.getAgeByIdCard(idCard) + "岁");
            String inpatientNo = erdao.getInpatientNo(idCard);
            if (StringUtil.notBlank(inpatientNo)) {
                info.setHospitalization(erdao.getDeptName(inpatientNo));
            }
        }
        String userCode = TokenUtil.getInstance().getTokenUserId();
        String userName = userCache.getEmployeeName(userCode);
        info.setCreateStaffCode(userCode);
        info.setCreateStaffName(userName);
        return info;
    }

    public Integer saveEmergencyRegInfo(EmergencyRegInfo info) {
        if (null != info.getId()) {
            erdao.updateById(info);
        } else {
            erdao.insert(info);
        }
        MzYshTzxx tzxx = new MzYshTzxx();
        tzxx.setPatientId(info.getPatientId());
        tzxx.setTimes(info.getTimes());
        tzxx.setValueMb(info.getPulse());
        String bldprs = info.getBloodPressure();
        if (null != bldprs && bldprs.contains("/")) {
            String[] xy = info.getBloodPressure().split("/");
            tzxx.setValueSsy(xy[0]);
            tzxx.setValueSzy(xy[1]);
        }
        tzxx.setValueTw(info.getTemperature());
        if (null == info.getRegTime()) {
            info.setRegTime(new Date());
        }
        tzxx.setVisitDate(info.getRegTime());
        saveMzVitalSigns(tzxx);
        return info.getId();
    }

    public List<EmergencyRegInfo> getEmrRegExportData(String start, String end) {
        QueryWrapper<EmergencyRegInfo> wrapper = new QueryWrapper<>();
        wrapper.ge("create_time", start);
        wrapper.le("create_time", end);
        return erdao.selectList(wrapper);
    }
}
