package thyyxxk.webserver.service.outpatient.triage;

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.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
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.*;
import thyyxxk.webserver.entity.socketmessage.ApiMessageBody;
import thyyxxk.webserver.service.externalhttp.WebSocketService;
import thyyxxk.webserver.utils.DateUtil;
import thyyxxk.webserver.utils.ResultVoUtil;
import thyyxxk.webserver.utils.StringUtil;
import thyyxxk.webserver.utils.TokenUtil;

import java.util.*;

/**
 * @author dj
 */
@Slf4j
@Service
public class TriageService {
    private final TriageDao dao;
    @Value("${triage-notify-url}")
    private String triageNotifyUrl;
    private final WebSocketService socketService;

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

    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);
        map.put("totalSize", iPage.getTotal());
        map.put("list", iPage.getRecords());
        return ResultVoUtil.success(map);
    }

    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, "请先前往【科室设置】分配分诊科室！");
        }
        return ResultVoUtil.success(dao.getTriagedPatients(depts));
    }

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

    public ResultVo<List<FloorScreen>> getBigScreenData(Boolean fullName) {
        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) {
            if (!fullName) {
                String name = item.getName();
                if (name.length() == 2) {
                    name = name.charAt(0) + "*";
                } else {
                    name = name.charAt(0) + "*" + name.substring(2);
                }
                item.setName(name);
            }
            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());
                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(), TokenUtil.getInstance().getTokenUserId(), room.getRoomCode());
        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);
        return ResultVoUtil.success(ret);
    }

    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());
        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("患者<span style=\"color:red;font-weight:bold\">%s</span>" +
                        "预约了国际医疗部，预约就诊时间为<span style=\"font-weight:bold;color: red\">%s</span>",
                vipRegMessage.getName(), 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()) {
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "没有国际医疗部的分诊人员。");
        }

        ApiMessageBody body = new ApiMessageBody(messageWrapper.toJSONString());
        for (String userCode : vipTriageUsers) {
            body.setUserCode(userCode);
            socketService.sendMessageByUserCode(body);
        }
        return ResultVoUtil.success();
    }
}
