package com.ywt.outpatient.controllers.baiyun;

import com.ywt.biz.common.constant.YwtCommonRespCode;
import com.ywt.biz.common.exception.YwtCommonException;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.DateUtil;
import com.ywt.biz.common.util.StringHelper;
import com.ywt.biz.common.util.serializers.JsonSerializer;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.doctor.*;
import com.ywt.gapi.system.*;
import com.ywt.gapi.third.taihe.GetScheduleListRequest;
import com.ywt.gapi.third.taihe.GetScheduleListResponse;
import com.ywt.gapi.third.taihe.Schedule;
import com.ywt.gapi.third.taihe.TaiheService;
import com.ywt.gapi.third.union_reg.*;
import com.ywt.outpatient.api.req.GetSubSpecialityReq;
import com.ywt.outpatient.api.resp.BaseResponse2;
import com.ywt.outpatient.api.resp.GetSubSpecialityResp;
import com.ywt.outpatient.api.resp.ListResponse;
import com.ywt.outpatient.core.BaseResponse;
import com.ywt.outpatient.core.annotations.UserLogin;
import com.ywt.outpatient.core.utils.BizUtil;
import com.ywt.outpatient.core.utils.CheckUtil;
import com.ywt.outpatient.core.utils.CollectionUtil;
import com.ywt.outpatient.core.Constant;
import com.ywt.outpatient.service.web.DoctorSrv;
import com.ywt.outpatient.service.web.SystemSrv;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 南方医院白云分院预约挂号接口
 */
@RestController("/baiyun/register")
@RequestMapping({"/baiyun/register"})
public class NfyyByfyRegisterController {
    private static Logger logger = LoggerFactory.getLogger(NfyyByfyRegisterController.class);


    @DubboReference
    private TaiheService taiheServiceBlockingStub;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private SystemSrv systemSrv;

    @Autowired
    private DoctorSrv doctorSrv;

    @DubboReference
    private DoctorService doctorServiceBlockingStub;

    @DubboReference
    private SystemService systemServiceBlockingStub;

    /**
     * 得到排班记录
     * <p>
     * 白云小程序使用
     */
    @UserLogin(need = false)
    @RequestMapping(value = {"/schedule"}, method = RequestMethod.POST)
    public BaseResponse getScheduleList(@RequestBody Map<String, Object> dataMap) {
        Integer hospitalId = (Integer) dataMap.getOrDefault("hospitalId", 0);
        String deptCode = (String) dataMap.getOrDefault("deptCode", "");
        String doctorCode = (String) dataMap.getOrDefault("doctorCode", "");
        String serviceDate = (String) dataMap.getOrDefault("serviceDate", "");
        String serviceCode = (String) dataMap.getOrDefault("serviceCode", "");// v1.6.5 新增，亚专科科室
        BaseResponse response = new BaseResponse();
        Map<String, Object> map;

        if (doctorCode.equals("0")) {
            doctorCode = "";
        }
        if (Checker.getIntegerValue(hospitalId) <= 0) {
            hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
        }

        try {
            response.setData(getScheduleListCommon(hospitalId, deptCode, doctorCode, serviceCode, -1, -1));
            return response.succeed("处理成功");
        } catch (YwtCommonException e) {
            return response.failed(BaseResponse.PARAMETER_ERROR, e.getMessage());
        } catch (Exception e) {
            logger.error("getScheduleList(){}", e.getMessage(), e);
            return response.error();
        }
    }

    /**
     * 原来的获取科室排班列表方法抽离
     *
     * @param startOffset 开始日期 offset, 值取 0 表示当天；
     * @param endOffset   结束日期 offset, 值取 0 表示当天；
     *                    <p>
     *                    startOffset 和 endOffset 其中一个为负值时，表示不指定日期范围，默认按照“太和查未来 8 天，不包括当天；白云查未来 6 天，包括当天”的规则查询。
     *                    都不为负值时，按照传入的 offset 计算开始结束日期。
     */
    private Map<String, Object> getScheduleListCommon(int hospitalId, String deptCode, String doctorCode,
                                                      String serviceCode, int startOffset, int endOffset) throws YwtCommonException {
        String redisKeyPattern = "SCHEDULE_LIST_%s_%s_%d_%s";
        String key = String.format(redisKeyPattern, deptCode, doctorCode, hospitalId, serviceCode);
        if (startOffset >= 0 && endOffset >= 0) {
            redisKeyPattern = "SCHEDULE_LIST_%s_%s_%d_%s_%d_%d";
            key = String.format(redisKeyPattern, deptCode, doctorCode, hospitalId, serviceCode, startOffset, endOffset);
        }
        String value = redisTemplate.opsForValue().get(key)+"";
        if (!StringHelper.isNullOrWhiteSpace(value)) {
            Map<String, Object> cacheMap = JsonSerializer.from(value, Map.class);
            if (cacheMap != null) {
                return cacheMap;
            }
        }

        List<Dept> deptList = systemSrv.getDeptListByHospitalId(hospitalId);
        if (deptList == null || deptList.size() == 0) {
            throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("无法获取科室数据(%d)", hospitalId));
        }
        List<Dept> tmpDeptList = deptList.stream().filter(p -> p.getDeptCode().equals(deptCode)).collect(Collectors.toList());
        if (tmpDeptList.size() == 0) {
            logger.error(String.format("deptCode 不存在(%d-%s-%s)", hospitalId, deptCode, doctorCode));
            throw new YwtCommonException(YwtCommonRespCode.P_ERR, "暂无出诊信息");
        }
        Dept secDept = tmpDeptList.get(0);
        tmpDeptList = deptList.stream().filter(p -> p.getDeptId() == secDept.getParentId()).collect(Collectors.toList());
        if (tmpDeptList.size() == 0) {
            logger.error(String.format("deptCode 不合法(%d-%s-%s)", hospitalId, deptCode, doctorCode));
            throw new YwtCommonException(YwtCommonRespCode.P_ERR, "暂无出诊信息");
        }

        Map<String, Object> map = new HashMap<>();
        map.put("hospitalId", hospitalId);
        map.put("hospitalName", BizUtil.getHospitalNameByIdSimply(hospitalId));
        map.put("deptCode", deptCode);
        map.put("deptName", secDept.getDeptName());

        List<DeptScheduleDoctor> doctorList = doctorSrv.getDeptScheduleDoctorListFromCache();
//        doctorList = doctorList.stream().filter(p -> p.getDeptCode().equals(deptCode)).collect(Collectors.toList());

        // 批量日期查询 HIS 接口
        boolean isTaihe = hospitalId == Constant.TAIHE_HOSPITAL_ID;
        if (startOffset < 0 || endOffset < 0) {
            startOffset = isTaihe ? 1 : 0;
            endOffset = isTaihe ? 9 : 7;
        }
        List<Map<String, Object>> lst = getConvertedScheduleList(hospitalId, startOffset, endOffset, deptCode,
                doctorCode, serviceCode, secDept.getDeptName());
        map.put("list", lst);
        redisTemplate.opsForValue().set(key, JsonSerializer.toJson(map), Constant.REDIS_EXPIRE_TIME_THREE_MINUTES, TimeUnit.SECONDS);
        return map;
    }

    private List<Map<String, Object>> getConvertedScheduleList(int hospitalId, int startOffset, int endOffset, String deptCode,
                                                               String doctorCode, String serviceCode, String deptName) {
        Calendar calendar = Calendar.getInstance();
        if (startOffset > 0) {
            calendar.add(Calendar.DATE, startOffset);
        }
        String startDate = DateUtil.formatDate(calendar.getTime(), "yyyy-MM-dd"); //日期
        calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, endOffset);
        String endDate = DateUtil.formatDate(calendar.getTime(), "yyyy-MM-dd"); //日期

        GetScheduleListRequest getScheduleListRequest = GetScheduleListRequest.newBuilder()
                .setDeptCode(deptCode)
                .setDoctorCode(doctorCode)
                .setServiceDate(startDate)
                .setServiceEndDate(endDate)
                .setHospitalId(hospitalId)
                .setServiceCode(serviceCode)
                .build();
        GetScheduleListResponse getScheduleListResponse = taiheServiceBlockingStub.getScheduleList(getScheduleListRequest);
        List<Schedule> scheduleList;
        if (getScheduleListResponse.getCode() == ResultCode.SUCCEED_VALUE) {
            scheduleList = getScheduleListResponse.getSchedulesList();
        } else {
            logger.error("NfyyByfyRegisterController#getConvertedScheduleList(): HIS 获取排班接口出错：{}", getScheduleListResponse.getMsg());
            scheduleList = new LinkedList<>();
        }
        // 单日查询的情况
        if (startOffset == endOffset) {
            Calendar c = Calendar.getInstance();
            if (startOffset > 0) {
                c.add(Calendar.DATE, startOffset);
            }
            String strDate = DateUtil.formatDate(c.getTime(), "yyyy-MM-dd"); //日期
            List<Map<String, Object>> list = new ArrayList<>();
            list.add(convertToScheduleMap(scheduleList, c, strDate, 0, hospitalId, deptName));
            return list;
        } else {
            return IntStream.range(startOffset, endOffset).mapToObj(offset -> {
                Calendar c = Calendar.getInstance();
                if (offset > 0) {
                    c.add(Calendar.DATE, offset);
                }
                String strDate = DateUtil.formatDate(c.getTime(), "yyyy-MM-dd"); //日期
                return convertToScheduleMap(scheduleList.stream().filter(s -> strDate.equals(s.getServiceDate())).collect(Collectors.toList()),
                        c, strDate, offset, hospitalId, deptName);
            }).collect(Collectors.toList());
        }
    }


    /**
     * 排班记录转为Map
     */
    private Map<String, Object> convertToScheduleMap(List<Schedule> scheduleList, Calendar calendar, String strDate,
                                                     int offset, int hospitalId, String deptName) {
        List<Map<String, Object>> forenoonList = new LinkedList<>();
        List<Map<String, Object>> afternoonList = new LinkedList<>();
        List<Map<String, Object>> nightList = new LinkedList<>();
        List<Map<String, Object>> afterMidnightList = new LinkedList<>();   // 下半夜排班
        List<Map<String, Object>> midnoonList = new ArrayList<>();          //午间排班记录
        GetDoctorByHisCodeResponse getDoctorByHisCodeResponse;
        String avatar;

        int doctorId;
        boolean hasScheduling = scheduleList.size() > 0;    //当期日期是否有排班

        Map<String, Object> groupMap = new HashMap<>();
        groupMap.put("serviceDate", strDate);
        groupMap.put("offset", offset);
        groupMap.put("week", DateUtil.getDayOfWeekShort(calendar));
        groupMap.put("deptName", deptName);

        for (Schedule s : scheduleList) {
//            if (doctorList.stream().noneMatch(p -> s.getDoctorCode().equals(p.getDoctorCode()))) {
//                continue;
//            }

            doctorId = 0;
            avatar = Constant.DOCTOR_AVATAR_URL;
            getDoctorByHisCodeResponse = getDoctorByHisCode(s.getDoctorCode(), hospitalId);

            if (getDoctorByHisCodeResponse.getResult().getCode() == ResultCode.SUCCEED) {
                if (!StringHelper.isNullOrWhiteSpace(getDoctorByHisCodeResponse.getAvatar())) {
                    avatar = getDoctorByHisCodeResponse.getAvatar();
                }

                doctorId = getDoctorByHisCodeResponse.getDoctorId();
            }
            if (doctorId <= 0) {
                continue;
            }
            // 需求改动：
            // v3.3.8 名医关闭咨询，也可以继续挂号和创建线下就诊订单
//            if (systemSrv.checkIsNetDept(s.getDeptCode())) {
//                //南方医院太和分院（互联网医院名医诊区）
//                GetConsultSettingsRequest getConsultSettingsReq = GetConsultSettingsRequest.newBuilder()
//                        .setUserId(doctorId)
//                        .build();
//                GetConsultSettingsResponse getConsultSettingsRes = doctorServiceBlockingStub.getConsultSettings(getConsultSettingsReq);
//                if (getConsultSettingsRes.getResult().getCode() != ResultCode.SUCCEED) {
//                    continue;
//                }
//                if (getConsultSettingsRes.getStatus() != 1) {
//                    //医生需要开启咨询功能
//                    continue;
//                }
//            }

            Map<String, Object> scheduleMap = new HashMap<>();
            scheduleMap.put("scheduleItemCode", s.getScheduleItemCode());       //门诊排班项记录标识
            scheduleMap.put("serviceDate", s.getServiceDate());                 //门诊排班日期(yyyy-MM-dd)
            scheduleMap.put("datePeriod", s.getDatePeriod());           //排班时段代码
            scheduleMap.put("datePeriodName", s.getDatePeriodName());   //排班时段名称
            scheduleMap.put("deptCode", s.getDeptCode());               //科室代码
            scheduleMap.put("deptName", s.getDeptName());               //科室名称
            scheduleMap.put("doctorCode", s.getDoctorCode());           //医生代码
            scheduleMap.put("doctorName", s.getDoctorName());           //医生名称
            scheduleMap.put("doctorId", doctorId);                      //医生Id
            scheduleMap.put("avatar", avatar);                          //医生图片链接
            scheduleMap.put("doctorTitle", s.getDoctorTitle());         //医生职称
            scheduleMap.put("doctorSpec", s.getDoctorSpec());           //医生专长
            scheduleMap.put("fee", s.getFee());                         //预约挂号总费用
            scheduleMap.put("regFee", s.getRegFee());                   //挂号费
            scheduleMap.put("checkupFee", s.getCheckupFee());           //诊查费
            scheduleMap.put("serviceFee", s.getServiceFee());           //服务费
            scheduleMap.put("otherFee", s.getOtherFee());               //其它费
            scheduleMap.put("admitAddress", s.getAdmitAddress());       //就诊地址
            scheduleMap.put("scheduleStatus", s.getScheduleStatus());       //出诊状态（1-可预约 0-已满 2-停诊 3-其它(不可预约)）
            scheduleMap.put("availableTotalNum", s.getAvailableTotalNum()); //该时段可预约的总号源数
            scheduleMap.put("availableLeftNum", s.getAvailableLeftNum());   //该时段剩余号源数
            scheduleMap.put("serviceName", s.getServiceName());   //亚专科名称
            scheduleMap.put("serviceCode", s.getServiceCode());   //亚专科代码

            String statusName = "";

            switch (s.getScheduleStatus()) {
                case 1:
                    statusName = "可预约";
                    break;
                case 0:
                    statusName = "已满";
                    break;
                case 2:
                    statusName = "停诊";
                    break;
                default:
                    statusName = "停诊";
                    break;
            }

            scheduleMap.put("scheduleStatusName", statusName);

            switch (s.getDatePeriod()) {
                case 2:
                    forenoonList.add(scheduleMap);
                    break;
                case 3:
                    afternoonList.add(scheduleMap);
                    break;
                case 4:
                    nightList.add(scheduleMap);
                    break;
                case 5:
                    afterMidnightList.add(scheduleMap);
                    break;
                case 6:                 //午间排班记录返回数
                    midnoonList.add(scheduleMap);
                    break;

            }
        }

        groupMap.put("hasScheduling", hasScheduling ? 1 : 0);   //当期日期是否有排班
        groupMap.put("forenoonSchedules", forenoonList);        //上午排班记录
        groupMap.put("afternoonSchedules", afternoonList);      //下午排班记录
        groupMap.put("nightSchedules", nightList);              //晚上排班记录
        groupMap.put("afterMidnightSchedules", afterMidnightList);              //晚上排班记录
        groupMap.put("midnoonSchedules", midnoonList);          //午间排班记录
        return groupMap;
    }


    private GetDoctorByHisCodeResponse getDoctorByHisCode(String doctorCode, int hospitalId) {
        GetDoctorByHisCodeRequest getDoctorByHisCodeRequest = GetDoctorByHisCodeRequest.newBuilder()
                .setHospitalId(hospitalId)
                .setHisCode(doctorCode)
                .build();
        return doctorServiceBlockingStub.getDoctorByHisCode(getDoctorByHisCodeRequest);
    }

    /**
     * 得到预约挂号公告
     *
     * @return {@link BaseResponse}
     */
    @RequestMapping(value = {"/getRegisteredAnnouncement"}, method = RequestMethod.POST)
    public BaseResponse getRegisteredAnnouncement(@RequestBody Map<String, Object> dataMap) {
        int hospitalId = (int) dataMap.getOrDefault("hospitalId", 0);
        int type = Checker.getIntegerValue((Integer) dataMap.getOrDefault("type", 1));// 兼容历史接口
        int deptId = Checker.getIntegerValue((Integer) dataMap.getOrDefault("deptId", 0));
        if (hospitalId <= 0) {
            return new BaseResponse().succeed("无数据");
        }
        RetRegisteredAnnouncementRequest req = RetRegisteredAnnouncementRequest.newBuilder()
                .setHospitalId(hospitalId)
                .setType(type)
                .setDeptId(deptId)
                .build();
        RetRegisteredAnnouncementResponse res = systemServiceBlockingStub.getRegisteredAnnouncement(req);
        if (res.getCode() != ResultCode.SUCCEED_VALUE) {
            return new BaseResponse().failed(res.getCode(), res.getInfo());
        }
        HashMap<Object, Object> map = new HashMap<>();
        map.put("id", res.getAnnouncementId());
        map.put("title", res.getTitle());
        map.put("content", res.getContent());
        return new BaseResponse().succeed(map);
    }

    /**
     * 获取亚专科列表
     */
    @RequestMapping(value = {"/subSpecialityList"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<ListResponse<GetSubSpecialityResp>> subSpecialityList(@RequestBody GetSubSpecialityReq reqData) {
        BaseResponse2<ListResponse<GetSubSpecialityResp>> baseResponse = new BaseResponse2<>();
        try {
            String deptCode = reqData.getDeptCode();
            if (StringHelper.isNullOrEmpty(deptCode)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "科室代码不能为空");
            int hospitalId = Checker.getIntegerValue(reqData.getHospitalId());
            if (Checker.getIntegerValue(hospitalId) <= 0) {
                hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            }
            Map<String, Object> map = new HashMap<>();
            boolean isToday = Checker.getBooleanValue(reqData.getIsToday());

            if(isToday){
                map = getScheduleListCommon(hospitalId, deptCode, "", "", 0, 0);
            } else {
                map = getScheduleListCommon(hospitalId, deptCode, "", "", -1, -1);
            }
            List<Map<String, Object>> data = (List<Map<String, Object>>) map.get("list");
            Set<GetSubSpecialityResp> set = tryGetSubSpecialitySet(data);
            ListResponse<GetSubSpecialityResp> resp = new ListResponse<GetSubSpecialityResp>();
            List<GetSubSpecialityResp> list = (CollectionUtil.toList(set));
            Collections.sort(list, (o1, o2) -> {
                Integer a = getNumberFromStr(Checker.getStringValue(o1.getServiceCode()));
                Integer b = getNumberFromStr(Checker.getStringValue(o2.getServiceCode()));
                return a.compareTo(b);
            });
            resp.setList(list);

            return baseResponse.succeed(resp);
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("NfyyByfyRegisterController#subSpecialityList(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    private int getNumberFromStr(String str) {
        if (StringHelper.isNullOrEmpty(str)) return 0;
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            logger.error("NfyyByfyRegisterController#subSpecialityList(): {}", e.getMessage(), e);
            return 0;
        }
    }


    /**
     * 查询医生联合排班
     */
    @RequestMapping(value = {"/doctorScheduleUnionList"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<GetDoctorScheduleUnionListResp> doctorScheduleUnionList(@RequestBody GetDoctorScheduleUnionListReq reqData) {
        BaseResponse2<GetDoctorScheduleUnionListResp> baseResponse = new BaseResponse2<>();
        try {
            int doctorId = reqData.getDoctorId();
            CheckUtil.ensureLargerThanZero(doctorId, "医生 id 有误");
            GetDoctorHisCodeListResponse response = doctorServiceBlockingStub.getDoctorHisCodeList(GetDoctorHisCodeListRequest.newBuilder()
                    .setDoctorId(doctorId)
                    .build());
            if (response.getResult().getCodeValue() != BaseResponse.SUCCEED) {
                return baseResponse.failed(BaseResponse.APP_ERROR, String.format("医生 id 无法获取信息(%d-%s)", doctorId, response.getResult().getInfo()));
            }
            List<DoctorHisCode> doctorHisCodes = response.getDoctorHisCodeList();
            List<ScheduleItem> unionItems = new ArrayList<>();
            String doctorName = "";
            for (DoctorHisCode doctorHisCode : doctorHisCodes) {
                doctorName = Checker.getStringValue(doctorHisCode.getDoctorName());
                int hospitalId = doctorHisCode.getHospitalId();
                String doctorCode = doctorHisCode.getDoctorCode();
                String deptCode = doctorHisCode.getDeptCode();
                List<Dept> deptList = systemSrv.getDeptListByHospitalId(hospitalId);
                // 默认查从当天开始共计 6 天
                Map<String, Object> data;
                try {
                    data = getScheduleListCommon(hospitalId, deptCode, doctorCode, "", 0, 6);
                } catch (YwtCommonException e) {
                    logger.error("NfyyByfyRegisterController#doctorScheduleUnionList(reqData={} ):\n {}", reqData, e.getMessage(), e);
                    continue;
                }
                List<Map<String, Object>> scheduleList = (List<Map<String, Object>>) data.getOrDefault("list", new ArrayList<>());
                    for (Map<String, Object> map : scheduleList) {
                        int offset = (int) map.getOrDefault("offset", 0);
                        String week = (String) map.getOrDefault("week", "");
                        String deptName = (String) map.getOrDefault("deptName", "");
                        List<Map<String, Object>> forenoonSchedules = tryGetDatePeriodScheduleList("forenoonSchedules", map);
                        List<Map<String, Object>> afternoonSchedules = tryGetDatePeriodScheduleList("afternoonSchedules", map);
                        List<Map<String, Object>> nightSchedules = tryGetDatePeriodScheduleList("nightSchedules", map);
                        List<Map<String, Object>> afterMidnightSchedules = tryGetDatePeriodScheduleList("afterMidnightSchedules", map);
                        List<ScheduleItem> forenoonScheduleItems = forenoonSchedules.stream().map(i -> tryGetScheduleItem(i, hospitalId, offset, week, deptList, deptName)).collect(Collectors.toList());
                        List<ScheduleItem> afternoonScheduleItems = afternoonSchedules.stream().map(i -> tryGetScheduleItem(i, hospitalId, offset, week, deptList, deptName)).collect(Collectors.toList());
                        List<ScheduleItem> nightScheduleItems = nightSchedules.stream().map(i -> tryGetScheduleItem(i, hospitalId, offset, week, deptList, deptName)).collect(Collectors.toList());
                        List<ScheduleItem> afterMidnightScheduleItems = afterMidnightSchedules.stream().map(i -> tryGetScheduleItem(i, hospitalId, offset, week, deptList, deptName)).collect(Collectors.toList());
                        unionItems.addAll(forenoonScheduleItems);
                        unionItems.addAll(afternoonScheduleItems);
                        unionItems.addAll(nightScheduleItems);
                        unionItems.addAll(afterMidnightScheduleItems);
                        // Add an empty list while no schedules
                        if (Checker.isNone(forenoonScheduleItems) && Checker.isNone(afternoonScheduleItems) &&
                                Checker.isNone(nightScheduleItems) && Checker.isNone(afterMidnightScheduleItems)) {
                            ScheduleItem i = new ScheduleItem();
                            Calendar calendar = Calendar.getInstance();
                            calendar.add(Calendar.DATE, offset);
                            String dateStr = DateUtil.formatDate(calendar.getTime(), "yyyy-MM-dd"); //日期
                            i.setServiceDate(dateStr);
                            i.setWeek(DateUtil.getDayOfWeekShort(calendar));
                            i.setOffset(offset);
                            i.setDeptName(deptName);
                            unionItems.add(i);
                        }
                    }
            }
            Map<String, List<ScheduleItem>> map = unionItems.stream().collect(Collectors.groupingBy(ScheduleItem::getServiceDate));
            GetDoctorScheduleUnionListResp resp = new GetDoctorScheduleUnionListResp();
            List<DoctorScheduleUnionItem> doctorScheduleUnionItems = new ArrayList<>();
            for (String k : map.keySet()) {
                List<ScheduleItem> schedules = map.get(k);
                ScheduleItem s = Checker.isNone(schedules) ? null : schedules.get(0);
                DoctorScheduleUnionItem i = new DoctorScheduleUnionItem();
                i.setOffset(Optional.ofNullable(s).map(ScheduleItem::getOffset).orElse(0));
                i.setWeek(Optional.ofNullable(s).map(ScheduleItem::getWeek).orElse(""));
                i.setDeptName(Optional.ofNullable(s).map(ScheduleItem::getDeptName).orElse(""));

                schedules = schedules.stream().filter(ss -> !StringHelper.isNullOrEmpty(ss.getDoctorCode())).collect(Collectors.toList());
                if (!Checker.isNone(schedules)) {
                    schedules.sort(Comparator.comparingInt(ScheduleItem::getPeriod));
                }
                int hasSchedule = Checker.isNone(schedules) ? 0 : 1;
                i.setServiceDate(k);
                i.setHasScheduling(hasSchedule);
                i.setScheduleItems(schedules);
                doctorScheduleUnionItems.add(i);
            }
            doctorScheduleUnionItems.sort(Comparator.comparingInt(DoctorScheduleUnionItem::getOffset));
            resp.setDoctorId(doctorId);
            resp.setDoctorName(doctorName);
            resp.setDoctorScheduleUnionItems(doctorScheduleUnionItems);
            return baseResponse.succeed(resp);

        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("NfyyByfyRegisterController#doctorScheduleUnionList(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    /**
     * 一些丑陋的方法，主要是从丑陋的数据里提取需要的信息
     */
    private Set<GetSubSpecialityResp> tryGetSubSpecialitySet(List<Map<String, Object>> data) {
        Set<GetSubSpecialityResp> ssrSet = new LinkedHashSet<>();
        // 添加一个门诊号
        GetSubSpecialityResp op = new GetSubSpecialityResp();
        op.setAvatar("https://ywt-image.oss-cn-shenzhen.aliyuncs.com/static/nfthnetwxmp/icon_expert_header_default.png");
        op.setServiceCode("");
        op.setServiceName("门诊号");
        ssrSet.add(op);
        for (Map<String, Object> m : data) {
            List<Map<String, Object>> forenoonSchedules = tryGetDatePeriodScheduleList("forenoonSchedules", m);
            for (Map<String, Object> mm : forenoonSchedules) {
                GetSubSpecialityResp r = tryGetSubSpecialityData(mm);
                if (r != null) {
                    ssrSet.add(r);
                }
            }
            List<Map<String, Object>> afternoonSchedules = tryGetDatePeriodScheduleList("afternoonSchedules", m);
            for (Map<String, Object> mm : afternoonSchedules) {
                GetSubSpecialityResp r = tryGetSubSpecialityData(mm);
                if (r != null) {
                    ssrSet.add(r);
                }
            }
            List<Map<String, Object>> nightSchedules = tryGetDatePeriodScheduleList("nightSchedules", m);
            for (Map<String, Object> mm : nightSchedules) {
                GetSubSpecialityResp r = tryGetSubSpecialityData(mm);
                if (r != null) {
                    ssrSet.add(r);
                }
            }
            List<Map<String, Object>> afterMidnightSchedules = tryGetDatePeriodScheduleList("afterMidnightSchedules", m);
            for (Map<String, Object> mm : afterMidnightSchedules) {
                GetSubSpecialityResp r = tryGetSubSpecialityData(mm);
                if (r != null) {
                    ssrSet.add(r);
                }
            }
        }
        return ssrSet;
    }

    private List<Map<String, Object>> tryGetDatePeriodScheduleList(String key, Map<String, Object> m) {
        if (m.get(key) == null) return new ArrayList<>();
        try {
            return (List<Map<String, Object>>) m.get(key);
        } catch (Exception e) {
            logger.error("NfyyByfyRegisterController#tryGetDatePeriodScheduleList(key={} , m={} ):\n {}", key, m, e.getMessage(), e);
        }
        return new ArrayList<>();
    }

    private GetSubSpecialityResp tryGetSubSpecialityData(Map<String, Object> mm) {
        if (mm == null) return null;
        String sn = (String) mm.getOrDefault("serviceName", "");
        String sc = (String) mm.getOrDefault("serviceCode", "");
        if (StringHelper.isNullOrEmpty(sc)) return null;
        GetSubSpecialityResp r = new GetSubSpecialityResp();
        r.setServiceName(sn);
        r.setServiceCode(sc);
        r.setAvatar("https://ywt-image.oss-cn-shenzhen.aliyuncs.com/static/nfthnetwxmp/icon_doctor_header_default.png");
        return r;
    }

    public ScheduleItem tryGetScheduleItem(Map<String, Object> mm, int hospitalId, int offset, String week, List<Dept> deptList, String deptName) {
        if (mm == null) return null;
        String serviceDate = (String) mm.getOrDefault("serviceDate", "");
        int period = (int) mm.getOrDefault("datePeriod", 0);
        String deptCode = (String) mm.getOrDefault("deptCode", "");
        String doctorCode = (String) mm.getOrDefault("doctorCode", "");

        int subHospitalId = 0;
        Dept dept = deptList.stream().filter(p -> p.getDeptCode().equals(deptCode)).findFirst().orElse(null);
        if (dept != null) {
            subHospitalId = Checker.getIntegerValue(dept.getSubHospitalId());
        }
        com.ywt.outpatient.api.union_reg.HospitalCustomInfo p = BizUtil.getHospitalCustomInfo(subHospitalId > 0 ? subHospitalId : hospitalId);
        String hospitalName = p.getAlias();
        String hospitalAddr = p.getAddr();
        ScheduleItem i = new ScheduleItem();
        i.setServiceDate(serviceDate);
        i.setPeriod(period);
        i.setHospitalName(hospitalName);
        i.setHospitalAddr(hospitalAddr);
        i.setDeptCode(deptCode);
        i.setHospitalId(hospitalId);
        i.setDoctorCode(doctorCode);
        i.setOffset(offset);
        i.setWeek(week);
        i.setDeptName(deptName);
        i.setSubHospitalId(subHospitalId);
        return i;
    }


    /**
     * 得到预约挂号公告
     *
     * @return {@link BaseResponse}
     */
    @RequestMapping(value = {"/getIntroduce"}, method = RequestMethod.POST)
    public BaseResponse getIntroduce(@RequestBody Map<String, Object> dataMap) {
        int subHospitalId = (int) dataMap.getOrDefault("subHospitalId", 0);
        int type = Checker.getIntegerValue((Integer) dataMap.getOrDefault("type", 1));// 兼容历史接口
        int deptId = Checker.getIntegerValue((Integer) dataMap.getOrDefault("deptId", 0));
        if (subHospitalId <= 0) {
            return new BaseResponse().succeed("无数据");
        }
        IntroduceRequest req = IntroduceRequest.newBuilder()
                .setHospitalId(subHospitalId)
                .setType(type)
                .setDeptId(deptId)
                .build();
        IntroduceResponse res = systemServiceBlockingStub.getIntroduce(req);
        if (res.getCode() != ResultCode.SUCCEED_VALUE) {
            return new BaseResponse().failed(res.getCode(), res.getInfo());
        }
        HashMap<Object, Object> map = new HashMap<>();
        map.put("id", res.getId());
        map.put("content", Checker.getStringValue(res.getContent()));
        return new BaseResponse().succeed(map);
    }

}
