package thyyxxk.webserver.service.outpatient.triage;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.dao.his.outpatient.triage.TriageDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.dictionary.CodeName;
import thyyxxk.webserver.entity.outpatient.triage.FloorScreen;
import thyyxxk.webserver.entity.outpatient.triage.MessageForPush;
import thyyxxk.webserver.entity.outpatient.triage.MzYshTzxx;
import thyyxxk.webserver.entity.outpatient.triage.MzfzPatientOrder;
import thyyxxk.webserver.entity.outpatient.triage.MzfzZdDeptRoom;
import thyyxxk.webserver.entity.outpatient.triage.TriageNotifyRelation;
import thyyxxk.webserver.entity.socketmessage.ApiMessageBody;
import thyyxxk.webserver.entity.socketmessage.SendUserList;
import thyyxxk.webserver.service.externalhttp.WebSocketService;
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
public class TriageService {
    private final TriageDao dao;
    private final WebSocketService socketService;
    private final SendWxInfoService sendWxInfoService;

    public TriageService(TriageDao dao, WebSocketService socketService, SendWxInfoService sendWxInfoService) {
        this.dao = dao;
        this.socketService = socketService;
        this.sendWxInfoService = sendWxInfoService;
    }

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

    public ResultVo<Map<String, Object>> getUnTriagedPatients(Integer currentPage, Integer pageSize) {
        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);
        IPage<MzfzPatientOrder> iPage = new Page<>(currentPage, pageSize);
        iPage = dao.getUnTriagedPatients(iPage, depts);

        List<MzfzPatientOrder> list = iPage.getRecords();
        fillGenderAndAge(list);
        map.put("totalSize", iPage.getTotal());
        map.put("list", list);
        return ResultVoUtil.success(map);
    }

    private void fillGenderAndAge(List<MzfzPatientOrder> list) {
        for (MzfzPatientOrder item : list) {
            if (IdCardUtil.isValidatedIdCard(item.getSocialNo())) {
                String gender = IdCardUtil.getGenderByIdCard(item.getSocialNo());
                item.setGender(gender.equals("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()));
        final String msg = JSON.toJSONString(messageForPush);
        ApiMessageBody messageBody = new ApiMessageBody(msg);
        messageBody.setSid(room.getTriageStaff());
        messageBody.setRoomCode(room.getRoomCode());
        socketService.sendFloorTriageMessage(messageBody);

        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);
        final String msg = JSON.toJSONString(messageForPush);
        ApiMessageBody messageBody = new ApiMessageBody(msg);
        messageBody.setSid(TokenUtil.getInstance().getTokenUserId());
        socketService.sendFloorTriageMessage(messageBody);
        return ResultVoUtil.success(ret);
    }

    public String refreshBigScreenData() {
        MessageForPush messageForPush = new MessageForPush();
        messageForPush.setAction(3);
        final String msg = JSON.toJSONString(messageForPush);
        ApiMessageBody messageBody = new ApiMessageBody(msg);
        messageBody.setSid(TokenUtil.getInstance().getTokenUserId());
        socketService.sendFloorTriageMessage(messageBody);
        return "OK";
    }

    public ResultVo<Map<String, Object>> getAllPatients(String searchContent, Integer currentPage, Integer pageSize) {
        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);
        searchContent = StringUtil.isBlank(searchContent) ? "%%" : searchContent + "%";
        IPage<MzfzPatientOrder> iPage = new Page<>(currentPage, pageSize);
        iPage = dao.getAllPatients(iPage, depts, searchContent);
        map.put("totalSize", iPage.getTotal());
        map.put("list", iPage.getRecords());
        return ResultVoUtil.success(map);
    }

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

    public ResultVo<String> saveMzVitalSigns(MzYshTzxx param) {
        log.info("保存生命体征：{}", param);
        if (null == dao.getMzVitalSigns(param.getPatientId())) {
            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()));
        String text = String.format("请%s号%s，到%s%s号诊室就诊。", messageForPush.getFzNo(), messageForPush.getName(),
                messageForPush.getDeptName(), messageForPush.getRoomNo());
        log.info("消息推送>>> {}\n{}", messageForPush, text);
        socketService.textToSpeech(text, String.valueOf(param.getSerialNo()));
        final String msg = JSON.toJSONString(messageForPush);
        TriageNotifyRelation notifyRelation = dao.selectTriageNotifyRelation(messageForPush.getSerialNo());
        if (null == notifyRelation) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有找到对应的分诊关系。");
        }
        ApiMessageBody messageBody = new ApiMessageBody(msg);
        messageBody.setSid(notifyRelation.getSocketSid());
        messageBody.setRoomCode(notifyRelation.getRoomCode());
        socketService.sendFloorTriageMessage(messageBody);
        dao.updateNotifyDatetime(param.getSerialNo());
        return socketService.sendRoomTriageMessage(messageBody);
    }

    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()) {
            socketService.sendUserListMessage(new SendUserList(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);
    }
}
