package thyyxxk.webserver.service.inpatient;

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.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.constants.Capacity;
import thyyxxk.webserver.constants.sidicts.Insutype;
import thyyxxk.webserver.constants.sidicts.MedType;
import thyyxxk.webserver.dao.his.inpatient.PatientDao;
import thyyxxk.webserver.dao.his.inpatient.Routines;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.YiZhuLuRuDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.dictionary.CodeName;
import thyyxxk.webserver.entity.inpatient.dismiss.ZyDisYbDiag;
import thyyxxk.webserver.entity.inpatient.patient.*;
import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.HuanZheFeiYong;
import thyyxxk.webserver.service.redislike.RedisLikeService;
import thyyxxk.webserver.utils.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author dj
 */
@Slf4j
@Service
public class PatientService {
    private static final String ZD_MEDCN = "yp_zd_dict";
    private final PatientDao dao;
    private final Routines routines;
    private final YiZhuLuRuDao yiZhuLuRuDao;
    private final RedisLikeService redis;

    @Autowired
    public PatientService(PatientDao dao, Routines routines, YiZhuLuRuDao yiZhuLuRuDao, RedisLikeService redis) {
        this.dao = dao;
        this.routines = routines;
        this.yiZhuLuRuDao = yiZhuLuRuDao;
        this.redis = redis;
    }

    public ResultVo<List<Overview>> getOverView(String ward) {
        ward = "%" + ward + "%";
        return ResultVoUtil.success(dao.getOverView(ward));
    }

    public ResultVo<Patient> getPatientInfo(String inpatientNo) {
        Patient data = dao.getPatientInfo(inpatientNo, "zy_actpatient");
        if (null == data) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有住院号【" + inpatientNo + "】的在院业务！");
        }
        data.setMedTypeName(MedType.getName(data.getMedType()));
        data.setInsutypeName(Insutype.getName(data.getInsutype()));
        data.setDutyNurseName(redis.getEmployeeName(data.getDutyNurse()));
        if (null != data.getSocialNo() && data.getSocialNo().trim().length() == 18 && !data.getSocialNo().startsWith("K")) {
            try {
                HuanZheFeiYong feiYongZhanBi = yiZhuLuRuDao.feiYongXinXi(data.getInpatientNo(), data.getAdmissTimes(), data.getLedgerSn());
                if (feiYongZhanBi != null) {
                    // 余额
                    data.setBalance(feiYongZhanBi.getBalance());
                    // 总费用
                    data.setTotalCharge(feiYongZhanBi.getTotalCharge());
                    // 药品 百分比
                    data.setYp(DecimalUtil.getPercent(Double.parseDouble(feiYongZhanBi.getYp()), Double.parseDouble(feiYongZhanBi.getTotalCharge())));
                    // 检验检查 百分比
                    data.setJyjc(DecimalUtil.getPercent(Double.parseDouble(feiYongZhanBi.getJyjc()), Double.parseDouble(feiYongZhanBi.getTotalCharge())));
                    // 医保 费用
                    data.setChargeYb(feiYongZhanBi.getChargeYb());
                    // 医保百分比
                    data.setYb(DecimalUtil.getPercent(Double.parseDouble(feiYongZhanBi.getChargeYb()), Double.parseDouble(feiYongZhanBi.getTotalCharge())));
                }
                // 是否出纳
                data.setTimesBilled(yiZhuLuRuDao.huoQuHuanZheXinXi(data.getInpatientNo(), data.getAdmissTimes()).getTimesBilled());
                data.setBirthDate(getBirthdayFromSocialNo(data.getSocialNo()));
                // 年龄
                data.setAge(DateUtil.calculateAge(new SimpleDateFormat("yyyy-MM-dd").parse(data.getBirthDate()), data.getAdmissDate()));
            } catch (ParseException e) {
                log.error("获取患者基本信息出错", e);
            }
        }
        return ResultVoUtil.success(data);
    }

    private String getBirthdayFromSocialNo(String socialNo) throws ParseException {
        String birth = socialNo.substring(6, 14);
        String year = birth.substring(0, 4);
        String month = birth.substring(4, 6);
        String day = birth.substring(6, 8);
        return year + "-" + month + "-" + day;
    }

    public ResultVo<HashMap<String, Object>> getDisDiag(String inpatientNo, Integer admissTimes) {
        HashMap<String, Object> map = new HashMap<>(Capacity.TWO);
        map.put("yb", dao.getYibaoDisDiags(inpatientNo, admissTimes));
        map.put("bl", dao.getBingliDisDiags(inpatientNo, admissTimes));
        return ResultVoUtil.success(map);
    }

    public ResultVo<Map<String, Object>> getNotUploadedFees(GetFeesParam param) {
        Integer ledgerSn = routines.selectMaxLedgerSn(param.getPatNo(), param.getTimes());
        if (null == ledgerSn) {
            ledgerSn = 1;
        }
        IPage<NotUploadedFee> iPage = new Page<>(param.getCurrentPage(), param.getPageSize());
        Map<String, Object> map = new HashMap<>(Capacity.THREE);
        String serial1 = "00";
        String serial2 = "";
        if (param.getZdTable().equals(ZD_MEDCN)) {
            serial1 = "01";
            serial2 = "99";
        }
        iPage = param.getInjuryMode() ? routines.selectInjuryNotUploadedFees(iPage, param.getZdTable(),
                param.getPatNo(), param.getTimes(), ledgerSn, DateUtil.getTodayEndTime(), serial1, serial2) :
                routines.selectNotUploadedFees(iPage, param.getZdTable(), param.getPatNo(), param.getTimes(),
                        ledgerSn, DateUtil.getTodayEndTime(), serial1, serial2);
        map.put("totalSize", iPage.getTotal());
        map.put("list", iPage.getRecords());
        map.put("sum", routines.selectSumChargeFee(param.getPatNo(), param.getTimes(), ledgerSn,
                DateUtil.getTodayEndTime(), serial1, serial2));
        return ResultVoUtil.success(map);
    }

    public ResultVo<Map<String, Object>> getIdCardInfo(String inpatientNo, Integer admissTimes) {
        List<IdCard> idCard = dao.getIdCard(inpatientNo, admissTimes);
        if (idCard.isEmpty()) {
            idCard = dao.getScanFile(inpatientNo, admissTimes);
        }
        if (idCard.isEmpty()) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者没有录入身份证或扫描件！");
        }
        Patient patient = dao.getPatientInfo(inpatientNo, "zy_actpatient");
        if (null == patient) {
            patient = dao.getPatientInfo(inpatientNo, "zy_inactpatient");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("idCard", idCard.get(0));
        map.put("patient", patient);
        return ResultVoUtil.success(map);
    }

    /**
     * requestType: 1 - 取消医保登记申请  2 - 出院结算申请
     */
    public ResultVo<List<ToYbkRequest>> getCancelRegisterRequests(String inpatientNo, Integer admissTimes, Integer requestType) {
        // settleType: 0-其他,  1-已结算未出纳， 4-已出纳，  3-中间结算
        Integer settleType = dao.getSettleType(inpatientNo, admissTimes);
        if (settleType > 1) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者已经出纳，不可进行取消医保登记的操作。");
        } else {
            return ResultVoUtil.success(dao.getRetractRequests(inpatientNo, admissTimes, requestType));
        }
    }

    public ResultVo<String> submitCancelRegisterRequest(ToYbkRequest param) {
        param.setInputId(TokenUtil.getTokenUserId());
        Integer count = dao.getRequestCount(param.getInpatientNo(),
                param.getAdmissTimes(), param.getRequestType());
        param.setInputTimes(++count);
        log.info("提交取消医保登记请求>>> {}", param);
        dao.submitNewRequest(param);
        return ResultVoUtil.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultVo<String> submitSiPatientYbInfo(SubmitSiPtnt param) {
        Patient patient = param.getPatient();
        patient.setStaffId(TokenUtil.getTokenUserId());
        MedType medType = MedType.get(patient.getMedType());
        if (null == medType) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请选择患者的医疗类别！");
        }
        if (medType == MedType.MATERNITY_HOSPITALIZATION) {
            if (StringUtil.isBlank(patient.getMatnType())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "生育类别不能为空！");
            }
            if (StringUtil.isBlank(patient.getLatechbFlag())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "是否晚育不能为空！");
            }
            if (StringUtil.isBlank(patient.getPretFlag())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "是否早产不能为空！");
            }
        }
        if (medType == MedType.MATERNITY_HOSPITALIZATION || medType == MedType.SINGLE_DISEASE_HOSPITALIZATION) {
            if (StringUtil.isBlank(patient.getDiseCode())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者病种不能为空！");
            }
        }
        if (StringUtil.isBlank(patient.getDutyNurse())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "责任护士不能为空！");
        }
        if (StringUtil.isBlank(patient.getCountry())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "国籍不能为空！");
        }
        if (StringUtil.isBlank(patient.getNation())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "民族不能为空！");
        }
        if (StringUtil.isBlank(patient.getContactName())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "联系人不能为空！");
        }
        if (StringUtil.isBlank(patient.getContactRelation())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "联系人关系不能为空！");
        }
        if (StringUtil.isBlank(patient.getContactAddrName())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "联系人地址不能为空！");
        }
        if (StringUtil.isBlank(patient.getContactPhone())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "联系人电话不能为空！");
        }
        if (!IdCardUtil.isValidatedIdCard(patient.getSocialNo())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "身份证不合法！");
        }
        int age = IdCardUtil.getAgeByIdCard(patient.getSocialNo());
        if (age < 16 || age > 60) {
            if (patient.getName().equals(patient.getContactName()) ||
                    patient.getContactRelation().equals("0")) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "16岁以下或60岁以上的患者，联系人不能填写本人。");
            }
        }

        String zyh = patient.getInpatientNo();
        int times = patient.getAdmissTimes();
        for (int i = 0; i < param.getDiags().size(); i++) {
            ZyInYbDiag diag = param.getDiags().get(i);
            diag.setInpatientNo(zyh);
            diag.setAdmissTimes(times);
        }

        patient.setResponceType(medType.getResponceType());
        log.info("【操作员：{}】保存患者信息：{} ", patient.getStaffId(), param);
        if (dao.isBatjBa2Exist(patient.getInpatientNo(), patient.getAdmissTimes()) == 0) {
            dao.createBatjBa2(patient.getInpatientNo(), patient.getAdmissTimes(), patient.getDutyNurse());
        } else {
            dao.updateBatjBa2(patient.getInpatientNo(), patient.getAdmissTimes(), patient.getDutyNurse());
        }
        dao.updateZyActpatient(patient);
        dao.updateAPatientMi(patient.getSocialNo(), patient.getInpatientNo(), patient.getCountry(), patient.getNation(),
                patient.getContactName(), patient.getContactRelation(), patient.getContactAddrName(), patient.getContactPhone());
        dao.deleteOldZyInYbDiag(zyh, times);
        if (ListUtil.notBlank(param.getDiags())) {
            dao.insertNewZyInYbDiag(patient.getStaffId(), param.getDiags());
        }
        return ResultVoUtil.success();
    }

    public ResultVo<List<ZyInYbDiag>> getZyInYbDiags(String inpatientNo, int admissTimes) {
        return ResultVoUtil.success(dao.getZyInYbDiag(inpatientNo, admissTimes));
    }

    public ResultVo<List<ZyDisYbDiag>> getZyDisYbDiags(String inpatientNo, int admissTimes) {
        return ResultVoUtil.success(dao.getZyDisYbDiags(inpatientNo, admissTimes));
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultVo<String> saveZyInYbDiags(List<ZyInYbDiag> list) {
        final String opId = TokenUtil.getTokenUserId();
        log.info("保存医保诊断表：操作员：{}，诊断：{}", opId, list);
        if (list == null || list.size() == 0) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有要保存的数据。");
        }
        final String inpatientNo = list.get(0).getInpatientNo();
        final Integer times = list.get(0).getAdmissTimes();
        dao.deleteOldZyInYbDiag(inpatientNo, times);
        dao.insertNewZyInYbDiag(opId, list);
        return ResultVoUtil.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultVo<String> saveSiZyInDiags(SaveSiZyDiags param) {
        param.setStaffId(TokenUtil.getTokenUserId());
        log.info("保存医保诊断表：操作员：{}，诊断：{}", param.getStaffId(), param);
        if (param.getDiags() == null || param.getDiags().size() == 0) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有要保存的数据。");
        }
        dao.deleteOldZyInYbDiag(param.getInpatientNo(), param.getAdmissTimes());
        dao.insertSiZyInYbDiag(param);
        return ResultVoUtil.success();
    }

    public ResultVo<String> genDismissActOrder(Patient param) {
        String disdate = dao.getActOrderDisDate(param.getInpatientNo(), param.getAdmissTimes());
        if (null != disdate) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者【" + param.getName() + "】已经存在出院医嘱！");
        }
        String staffId = TokenUtil.getTokenUserId();
        float orderNo = dao.getActOrderNo();
        dao.updateActorOrderNo(orderNo + 1);
        dao.insertNewDismissActOrder(orderNo, param.getInpatientNo(), param.getAdmissTimes(),
                param.getDismissOrderDate(), staffId, param.getAdmissWard());
        List<ZyDisYbDiag> zyDisYbDiags = dao.getDisDiags(param.getInpatientNo(), param.getAdmissTimes());
        if (null == zyDisYbDiags || zyDisYbDiags.isEmpty()) {
            List<ZyInYbDiag> inYbDiags = dao.getZyInYbDiag(param.getInpatientNo(), param.getAdmissTimes());
            if (null == inYbDiags || inYbDiags.isEmpty()) {
                CodeName diag = dao.getAdmissDiag(param.getInpatientNo(), param.getAdmissTimes());
                dao.insertDisDiag(param.getInpatientNo(), param.getAdmissTimes(), diag.getCode(),
                        diag.getName(), staffId);
            } else {
                inYbDiags.forEach(itm -> {
                    itm.setSiDiagType(1);
                });
                dao.insertDisDiags(staffId, inYbDiags);
            }
        }
        return ResultVoUtil.success(String.valueOf(orderNo));
    }

    public ResultVo<String> receiveAndRecalculateCost(Patient p) {
        String zyh = p.getInpatientNo();
        int times = p.getAdmissTimes();
        int infant = zyh.startsWith("$") ? 1 : 0;
        int ledger = p.getLedgerSn();
        dao.correctFeeChargeTimeBeforeAdmiss(zyh, times, p.getAdmissDate());
        Date disdate = dao.selectActOrderDisDate(zyh, times);
        if (null != disdate) {
            dao.correctFeeChargeTimeAfterDismiss(zyh, times, disdate);
        }
        dao.recountDeposit(zyh, times, ledger);
        dao.zyReceiveDrug(zyh, times, infant);
        dao.zyReceiveOne(zyh, times, infant);
        dao.zyCnglFyjsListYz(zyh, times);
        dao.updateZyDetailCharge(zyh, times, ledger);
        dao.zyCalcDetailAgainNew(zyh, times, ledger);
        return ResultVoUtil.success("费用接收重算成功。");
    }
}
