package com.openhis.web.ybmanage.service.impl;

import java.math.BigDecimal;
import java.time.*;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.core.common.core.domain.R;
import com.core.common.enums.DeleteFlag;
import com.core.common.enums.TenantOptionDict;
import com.core.common.exception.ServiceException;
import com.core.common.utils.StringUtils;
import com.core.web.util.TenantOptionUtil;
import com.openhis.common.enums.ParticipantType;
import com.openhis.common.enums.TherapyTimeType;
import com.openhis.common.enums.Whether;
import com.openhis.common.enums.ybenums.*;
import com.openhis.web.ybmanage.mapper.YbMapper;
import com.openhis.web.ybmanage.service.IYbService;
import com.openhis.yb.dto.*;
import com.openhis.yb.service.YbHttpUtils;

/**
 * 医保ServiceImpl
 *
 * @author Thanking
 */
@Service
public class YbServiceImpl implements IYbService {

    @Autowired
    private YbMapper ybMapper;
    @Autowired
    private YbHttpUtils ybHttpUtils;

    /**
     * 【3101】明细审核事前事中分析服务
     *
     * @param encounterId 就诊ID
     * @param trigScen 触发场景
     * @return 结果
     */
    @Override
    public R<?> yb3101PreMidAnalysis(Long encounterId, String trigScen) {
        // 验证触发场景
        if (YbTrigScen.getByValue(trigScen) == null) {
            throw new ServiceException("请指定触发场景");
        }

        // 查询就诊患者信息
        Yb3101AdapterEncounterPatientInfo encounterPatientInfo = ybMapper.yb3101SelectEncounterPatientInfo(encounterId,
            Whether.YES.getValue(), ParticipantType.REGISTRATION_DOCTOR.getCode(), DeleteFlag.NOT_DELETED.getCode());
        if (encounterPatientInfo == null) {
            throw new ServiceException("患者信息不存在");
        }

        // 查询就诊诊断信息
        List<Yb3101AdapterEncounterDiagnoseInfo> encounterDiagnoseInfoList =
            ybMapper.yb3101SelectEncounterDiagnoseInfo(encounterId, DeleteFlag.NOT_DELETED.getCode());
        if (encounterDiagnoseInfoList.isEmpty()) {
            throw new ServiceException("诊断信息不存在");
        }

        // 查询处方（医嘱）信息
        List<Yb3101AdapterOrderInfo> orderInfoList = ybMapper.yb3101SelectOrderInfo(encounterId,
            DeleteFlag.NOT_DELETED.getCode(), YbMedChrgItmType.WEST_MEDICINE.getValue(),
            YbMedChrgItmType.CHINESE_MEDICINE_SLICES_FEE.getValue(), YbMedChrgItmType.CHINESE_MEDICINE_FEE.getValue());
        if (encounterDiagnoseInfoList.isEmpty()) {
            throw new ServiceException("处方信息不存在");
        }

        // 作成诊断信息集合
        List<Yb3101InputFsiDiagnoseDto> fsiDiagnoseDtos =
            encounterDiagnoseInfoList.stream().map(this::createYb3101FsiDiagnoseDto).collect(Collectors.toList());

        // 作成处方(医嘱)信息集合
        List<Yb3101InputFsiOrderDto> fsiOrderDtos =
            orderInfoList.stream().map(this::createYb3101InputFsiOrderDto).collect(Collectors.toList());

        // 作成就诊信息
        Yb3101InputFsiEncounterDto fsiEncounterDto =
            createYb3101InputFsiEncounterDto(encounterPatientInfo, fsiDiagnoseDtos, fsiOrderDtos);

        // 作成参保人信息
        Yb3101InputPatientDto patientDto = createYb3101InputPatientDto(encounterPatientInfo, fsiEncounterDto);

        // 作成规则分析信息
        Yb3101InputData data = createYb3101InputData(patientDto, trigScen);

        // 向医保服务发起HTTP请求，获取返回结果
        Yb3101OutputResult result = ybHttpUtils.yb3101PreMidAnalysis(data);

        // TODO:后续处理需等待门诊住院开发完全后

        return R.ok();
    }

    /**
     * 【3103】事前事中服务反馈服务
     *
     * @param warnRsltId 违规标识
     * @param dspoWay 处理方式
     * @param dspoWayRea 处理原因
     * @return 结果
     */
    @Override
    public R<?> yb3103PreMidFeedback(String warnRsltId, String dspoWay, String dspoWayRea) {
        // 验证处理方式
        if (YbDspoWay.getByValue(dspoWay) == null) {
            throw new ServiceException("请指定处理方式");
        }

        // 继续执行医嘱时，处理原因必填
        if (YbDspoWay.CONTINUE.getValue().equals(dspoWay) && StringUtils.isEmpty(dspoWayRea)) {
            throw new ServiceException("请填写处理原因");
        }

        // 作成处理数据
        Yb3103InputWarn warn =
            new Yb3103InputWarn().setWarnRsltId(warnRsltId).setDspoWay(dspoWay).setDspoWayRea(dspoWayRea);

        // 作成输入
        Yb3103InputData data = new Yb3103InputData().setWarnType(YbWarnType.PRE.getValue()).setWarns(List.of(warn));

        // 向医保服务发起HTTP请求
        ybHttpUtils.yb3103PreMidFeedback(data);

        return R.ok();
    }

    /**
     * 【4101A】医疗保障基金结算清单信息上传（新）
     *
     * @param paymentId 付款ID
     * @return 结果
     */
    @Override
    public R<?> yb4101aUploadFundSettle(Long paymentId) {
        // 查询结算清单信息
        List<Yb4101AAdapterSettleInfo> settleInfoList = ybMapper.yb4101aSelectSettleInfo(paymentId);

        return R.ok();
    }

    /**
     * 【3101】创建诊断信息
     * 
     * @param encounterDiagnoseInfo 就诊诊断信息
     * @return 诊断信息
     */
    private Yb3101InputFsiDiagnoseDto
        createYb3101FsiDiagnoseDto(Yb3101AdapterEncounterDiagnoseInfo encounterDiagnoseInfo) {
        // 根据住院患者疾病诊断类型代码，判断出入诊断类别，非入院诊断，视为出院诊断
        String inoutDiseType;
        if (YbIptDiseTypeCode.ADMISSION_DIAGNOSIS.getCode().equals(encounterDiagnoseInfo.getIptDiseTypeCode())) {
            inoutDiseType = YbInOutDiagType.YB_IN_DIAG_TYPE.getValue();
        } else {
            inoutDiseType = YbInOutDiagType.YB_OUT_DIAG_TYPE.getValue();
        }
        // 返回诊断信息
        return new Yb3101InputFsiDiagnoseDto()
            // 诊断标识：诊断记录唯一标识
            .setDiseId(encounterDiagnoseInfo.getDiseId())
            // 出入诊断类别：参考字典表
            .setInoutDiseType(inoutDiseType)
            // 主诊断标志：参考字典表
            .setMaindiseFlag(encounterDiagnoseInfo.getMaindiseFlag())
            // 诊断排序号：例如：1,2,3…
            .setDiasSrtNo(encounterDiagnoseInfo.getDiasSrtNo())
            // 诊断(疾病)编码
            .setDiseCodg(encounterDiagnoseInfo.getDiseCodg())
            // 诊断(疾病)名称
            .setDiseName(encounterDiagnoseInfo.getDiseName())
            // 诊断日期：格式：yyyy-MM-dd HH:mm:ss
            .setDiseDate(encounterDiagnoseInfo.getDiseDate());
    }

    /**
     * 【3101】创建处方（医嘱）信息
     *
     * @param orderInfo 处方（医嘱）信息
     * @return 处方（医嘱）信息
     */
    private Yb3101InputFsiOrderDto createYb3101InputFsiOrderDto(Yb3101AdapterOrderInfo orderInfo) {
        // 根据治疗类型，判断是否为长期医嘱
        String longDrordFlag;
        if (TherapyTimeType.LONG_TERM.getValue().equals(orderInfo.getTherapyEnum())) {
            longDrordFlag = Whether.YES.getCode();
        } else {
            longDrordFlag = Whether.NO.getCode();
        }
        // 根据医保类别，判断目录类别
        String hilistType = null;
        if (YbMedChrgItmType.WEST_MEDICINE.getValue().equals(orderInfo.getYbType())
            || YbMedChrgItmType.CHINESE_MEDICINE_FEE.getValue().equals(orderInfo.getYbType())) {
            hilistType = YbListType.WESTERN_AND_CHINESE_PATENT_MEDICINE.getValue();
        } else if (YbMedChrgItmType.CHINESE_MEDICINE_SLICES_FEE.getValue().equals(orderInfo.getYbType())) {
            hilistType = YbListType.IMPORTANT_HERBAL_SLICES.getValue();
        }
        return new Yb3101InputFsiOrderDto()
            // 处方(医嘱)标识：处方(医嘱)记录唯一ID
            .setRxId(orderInfo.getRxId())
            // 处方号
            .setRxno(orderInfo.getRxno())
            // 组编号
            .setGrpno(orderInfo.getGrpno())
            // 是否为长期医嘱：[1=是,0=否]
            .setLongDrordFlag(longDrordFlag)
            // 目录类别：参考字典表
            .setHilistType(hilistType)
            // 收费类别：参考字典表
            .setChrgType(orderInfo.getYbType())
            // 医嘱行为：参考字典表
            .setDrordBhvr(YbDrordBhvr.OTHER.getValue())
            // 医保目录代码：国家统一标准编码
            .setHilistCode(orderInfo.getHilistCode())
            // 医保目录名称：国家统一标准名称
            .setHilistName(orderInfo.getHilistName())
            // 医保目录(药品)剂型：国家统一标准药品剂型
            .setHilistDosform(orderInfo.getHilistDosform())
            // 医保目录等级
            .setHilistLv(orderInfo.getHilistLv())
            // 医保目录价格，暂时取本系统单价
            .setHilistPric(orderInfo.getPric())
            // 医院目录代码
            .setHosplistCode(orderInfo.getHilistCode())
            // 医院目录名称
            .setHosplistName(orderInfo.getHilistName())
            // 医院目录(药品)剂型
            .setHosplistDosform(orderInfo.getHilistDosform())
            // 数量
            .setCnt(BigDecimal.valueOf(orderInfo.getCnt()))
            // 单价
            .setPric(orderInfo.getPric())
            // 总费用
            .setSumamt(orderInfo.getSumamt())
            // 自费金额
            .setOwnpayAmt(BigDecimal.ZERO) // TODO:从哪取啊，住院有（但表还没建），门诊没有
            // 自付金额
            .setSelfpayAmt(BigDecimal.ZERO) // TODO:从哪取啊，住院有（但表还没建），门诊没有
            // 规格：例如：0.25g×12片/盒
            .setSpec(orderInfo.getSpec())
            // 数量单位：例如：盒
            .setSpecUnt(orderInfo.getSpecUnt())
            // 医嘱开始日期：格式：yyyy-MM-dd HH:mm:ss
            .setDrordBegnDate(orderInfo.getDrordBegnDate())
            // 下达医嘱的科室标识
            .setDrordDeptCodg(orderInfo.getDrordDeptCodg())
            // 下达医嘱科室名称
            .setDrordDeptName(orderInfo.getDrordDeptName())
            // 开处方(医嘱)医生标识
            .setDrordDrCodg(orderInfo.getDrordDrCodg())
            // 开处方(医嘱)医生姓名
            .setDrordDrName(orderInfo.getDrordDrName())
            // 开处方(医嘱)医职称：参考字典表
            .setDrordDrProfttl(orderInfo.getDrordDrProfttl())
            // 是否当前处方(医嘱)：本次处方(医嘱)标记[1=是,0=否]
            .setCurrDrordFlag(Whether.YES.getCode());
    }

    /**
     * 【3101】创建就诊信息
     * 
     * @param encounterPatientInfo 就诊患者信息
     * @param fsiDiagnoseDtos 诊断信息集合
     * @param fsiOrderDtos 处方(医嘱)信息集合
     * @return 就诊信息
     */
    private Yb3101InputFsiEncounterDto createYb3101InputFsiEncounterDto(
        Yb3101AdapterEncounterPatientInfo encounterPatientInfo, List<Yb3101InputFsiDiagnoseDto> fsiDiagnoseDtos,
        List<Yb3101InputFsiOrderDto> fsiOrderDtos) {
        // 判断出院时间，如果为空，默认当天下午四点
        if (encounterPatientInfo.getDscgDate() == null) {
            encounterPatientInfo.setDscgDate(getFourPmDate(encounterPatientInfo.getAdmDate()));
        }
        // 提取主诊断
        Yb3101InputFsiDiagnoseDto mainDiagnose =
            fsiDiagnoseDtos.stream().filter(e -> Whether.YES.getCode().equals(e.getMaindiseFlag())).findFirst()
                .orElse(new Yb3101InputFsiDiagnoseDto());
        // 计算总费用
        BigDecimal medfeeSumamt =
            fsiOrderDtos.stream().map(Yb3101InputFsiOrderDto::getSumamt).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 作成就诊信息
        return new Yb3101InputFsiEncounterDto()
            // 就诊标识：就诊记录唯一ID
            .setMdtrtId(encounterPatientInfo.getMdtrtId())
            // 医疗服务机构标识：定点医疗机构ID
            .setMedinsId(encounterPatientInfo.getMedinsId())
            // 医疗机构名称
            .setMedinsName(encounterPatientInfo.getMedinsName())
            // 医疗机构行政区划编码
            .setMedinsAdmdvs(encounterPatientInfo.getMedinsAdmdvs())
            // 医疗服务机构类型：参考字典表
            .setMedinsType(YbMedinsType.FIXMEDINS_TYPE1.getValue())
            // 医疗机构等级：参考字典表
            .setMedinsLv(TenantOptionUtil.getOptionContent(TenantOptionDict.YB_MEDINS_LV))
            // 入院日期
            .setAdmDate(encounterPatientInfo.getAdmDate())
            // 出院日期
            .setDscgDate(encounterPatientInfo.getDscgDate())
            // 主诊断编码：例如：I63.9
            .setDscgMainDiseCodg(mainDiagnose.getDiseCodg())
            // 主诊断名称：例如：脑梗塞
            .setDscgMainDiseName(mainDiagnose.getDiseName())
            // 诊断信息集合
            .setFsiDiagnoseDtos(fsiDiagnoseDtos)
            // 医师标识：医生唯一ID
            .setDrCodg(encounterPatientInfo.getDrCodg())
            // 入院科室标识：科室唯一ID
            .setAdmDeptCodg(encounterPatientInfo.getAdmDeptCodg())
            // 入院科室名称
            .setAdmDeptName(encounterPatientInfo.getAdmDeptName())
            // 出院科室标识：科室唯一ID
            .setDscgDeptCodg(encounterPatientInfo.getDscgDeptCodg())
            // 出院科室名称
            .setDscgDeptName(encounterPatientInfo.getDscgDeptName())
            // 就诊类型：参考字典表
            .setMedMdtrtType(Optional.ofNullable(YbMedType.getByValue(encounterPatientInfo.getMedType()))
                .orElse(YbMedType.GENERAL_OUTPATIENT).getYbMedMdtrtType().getValue())
            // 医疗类别：参考字典表
            .setMedType(encounterPatientInfo.getMedType())
            // 处方(医嘱)信息集合
            .setFsiOrderDtos(fsiOrderDtos)
            // 生育状态：参考字典表
            .setMatnStas(YbMatnStas.UNKNOWN.getValue())
            // 总费用
            .setMedfeeSumamt(medfeeSumamt)
            // 自费金额
            .setOwnpayAmt(encounterPatientInfo.getOwnpayAmt())
            // 自付金额
            .setSelfpayAmt(encounterPatientInfo.getSelfpayAmt())
            // 结算总次数
            .setSetlTotlnum(encounterPatientInfo.getSetlTotlnum())
            // 险种：参考字典表
            .setInsutype(encounterPatientInfo.getInsutype())
            // 报销标志：参考字典表
            .setReimFlag(YbPayLoc.PAY_LOC1.getValue())
            // 异地结算标志：参考字典表
            .setOutSetlFlag(Whether.NO.getCode());
    }

    /**
     * 【3101】创建参保人信息
     * 
     * @param encounterPatientInfo 就诊患者信息
     * @param fsiEncounterDto 就诊信息
     * @return 参保人信息
     */
    private Yb3101InputPatientDto createYb3101InputPatientDto(Yb3101AdapterEncounterPatientInfo encounterPatientInfo,
        Yb3101InputFsiEncounterDto fsiEncounterDto) {
        // 作成参保人信息
        return new Yb3101InputPatientDto()
            // 参保人标识：参保人唯一ID
            .setPatnId(encounterPatientInfo.getPatnId())
            // 姓名
            .setPatnName(encounterPatientInfo.getPatnName())
            // 性别：参考字典表
            .setGend(encounterPatientInfo.getGend())
            // 出生日期：格式：yyyy-MM-dd
            .setBrdy(encounterPatientInfo.getBrdy())
            // 统筹区编码：参保人所属统筹区
            .setPoolarea(encounterPatientInfo.getPoolarea())
            // 当前就诊标识：本次就诊记录唯一ID
            .setCurrMdtrtId(encounterPatientInfo.getMdtrtId())
            // 就诊信息集合
            .setFsiEncounterDtos(List.of(fsiEncounterDto));
    }

    /**
     * 创建规则分析信息
     * 
     * @param patientDto 参保人信息
     * @param trigScen 触发场景
     * @return 规则分析信息
     */
    private Yb3101InputData createYb3101InputData(Yb3101InputPatientDto patientDto, String trigScen) {
        // 作成规则分析信息
        return new Yb3101InputData()
            // 系统编码：调用方系统简码标识
            .setSyscode("sys01")
            // 参保人信息
            .setPatientDtos(List.of(patientDto))
            // 触发场景：此值与ruleIds指定其一即可,请优先指定此值
            .setTrigScen(trigScen);

    }

    /**
     * 获取指定日期当天的下午四点（16:00）的日期
     * 
     * @param inputDate 指定日期
     * @return 当天的下午四点（16:00）的日期
     */
    private Date getFourPmDate(Date inputDate) {
        ZonedDateTime inputZdt = inputDate.toInstant().atZone(ZoneId.systemDefault());
        LocalDate datePart = inputZdt.toLocalDate();
        LocalTime fourPm = LocalTime.of(16, 0);
        LocalDateTime fourPmLocalDateTime = LocalDateTime.of(datePart, fourPm);
        ZonedDateTime fourPmZdt = fourPmLocalDateTime.atZone(ZoneId.systemDefault());
        return Date.from(fourPmZdt.toInstant());
    }
}
