package com.openhis.web.inhospitalcharge.appservice.impl;

import java.util.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.*;
import com.openhis.administration.domain.*;
import com.openhis.administration.service.*;
import com.openhis.clinical.domain.Condition;
import com.openhis.clinical.service.IConditionService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.enums.ybenums.YbIptDiseTypeCode;
import com.openhis.common.enums.ybenums.YbPayment;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.financial.domain.PaymentRecDetail;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.service.IPaymentRecDetailService;
import com.openhis.financial.service.IPaymentReconciliationService;
import com.openhis.web.inhospitalcharge.appservice.IInHospitalRegisterAppService;
import com.openhis.web.inhospitalcharge.dto.*;
import com.openhis.web.inhospitalcharge.mapper.InHospitalRegisterAppMapper;
import com.openhis.web.patientmanage.appservice.IPatientInformationService;
import com.openhis.web.patientmanage.dto.PatientInformationDto;
import com.openhis.yb.service.YbManager;

import lombok.extern.slf4j.Slf4j;

/**
 * 住院登记 应用实现类
 */
@Slf4j
@Service
public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppService {

    @Resource
    AssignSeqUtil assignSeqUtil;

    @Resource
    InHospitalRegisterAppMapper inHospitalRegisterAppMapper;

    @Resource
    IEncounterService iEncounterService;

    @Resource
    IEncounterParticipantService iEncounterParticipantService;

    @Resource
    IConditionService iConditionService;

    @Resource
    IEncounterDiagnosisService iEncounterDiagnosisService;

    @Resource
    IEncounterLocationService iEncounterLocationService;

    @Resource
    IAccountService iAccountService;

    @Resource
    IPatientInformationService patientInformationService;

    @Resource
    IPaymentReconciliationService iPaymentReconciliationService;

    @Resource
    IPaymentRecDetailService iPaymentRecDetailService;

    @Resource
    IPatientService iPatientService;

    @Resource
    private YbManager ybManager;

    /**
     * 门诊医生开住院申请
     *
     * @param inHospitalRegisterDto 住院登记参数
     * @return 结果
     */
    @Override
    public R<?> registerByDoctor(InHospitalRegisterDto inHospitalRegisterDto) {
        // 当前账号的参与者id
        Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
        // 患者id
        Long patientId = inHospitalRegisterDto.getPatientId();
        // 门诊就诊id
        Long ambEncounterId = inHospitalRegisterDto.getAmbEncounterId();
        // 获取执行科室
        LambdaQueryWrapper<Encounter> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Encounter::getAmbEncounterId, ambEncounterId);
        if (!iEncounterService.list(queryWrapper).isEmpty()) {
            return R.fail(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00008, null));
        }

        // 住院encounter
        Encounter zYEncounter = new Encounter();
        zYEncounter.setPatientId(patientId); // 患者id
        zYEncounter.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.ADMISSION_NUM.getPrefix(), 4));// 住院编号
        zYEncounter.setPriorityEnum(inHospitalRegisterDto.getPriorityEnum()); // 优先级
        zYEncounter.setStatusEnum(EncounterZyStatus.TO_BE_REGISTERED.getValue()); // 状态为待登记
        zYEncounter.setClassEnum(EncounterClass.IMP.getValue()); // 类别为住院
        zYEncounter.setYbClassEnum(inHospitalRegisterDto.getYbClassEnum()); // 医保类别
        zYEncounter.setOrganizationId(inHospitalRegisterDto.getInHospitalOrgId()); // 住院科室id
        zYEncounter.setAmbEncounterId(ambEncounterId); // 门诊就诊id
        iEncounterService.save(zYEncounter);
        Long zYEncounterId = zYEncounter.getId();// 住院就诊id

        // 住院encounter的参与者
        EncounterParticipant zYEncounterParticipant = new EncounterParticipant();
        zYEncounterParticipant.setEncounterId(zYEncounterId); // 住院就诊id
        zYEncounterParticipant.setTypeCode(ParticipantType.ADMITTER.getCode()); // 接诊医生
        zYEncounterParticipant.setPractitionerId(practitionerId); // 参与者id
        iEncounterParticipantService.save(zYEncounterParticipant);

        // 住院诊断
        Condition zYCondition = new Condition();
        zYCondition.setVerificationStatusEnum(ConditionVerificationStatus.CONFIRMED.getValue()); // 验证状态
        zYCondition.setPatientId(patientId); // 患者id
        zYCondition.setDefinitionId(inHospitalRegisterDto.getDiagnosisDefinitionId()); // 诊断定义Id
        zYCondition.setYbNo(inHospitalRegisterDto.getDiagnosisYbNo()); // 诊断对应的医保编码
        zYCondition.setRecordedDatetime(new Date()); // 记录时间
        zYCondition.setRecorderId(SecurityUtils.getLoginUser().getPractitionerId());// 记录人
        iConditionService.save(zYCondition);
        Long conditionId = zYCondition.getId();// 诊断id

        EncounterDiagnosis zYEncounterDiagnosis = new EncounterDiagnosis();
        zYEncounterDiagnosis.setEncounterId(zYEncounterId); // 住院就诊id
        zYEncounterDiagnosis.setConditionId(conditionId); // 诊断id
        zYEncounterDiagnosis.setMaindiseFlag(Whether.YES.getValue()); // 主诊断
        zYEncounterDiagnosis.setMedTypeCode(inHospitalRegisterDto.getMedTypeCode());// 医疗类型
        zYEncounterDiagnosis.setDiagnosisDesc(inHospitalRegisterDto.getDiagnosisDesc()); // 诊断描述
        zYEncounterDiagnosis.setIptDiseTypeCode(YbIptDiseTypeCode.ADMISSION_DIAGNOSIS.getValue()); // 住院患者疾病诊断类型代码
        iEncounterDiagnosisService.save(zYEncounterDiagnosis);

        // 病区
        if (inHospitalRegisterDto.getWardLocationId() != null) {
            EncounterLocation zYEncounterLocation = new EncounterLocation();
            zYEncounterLocation.setEncounterId(zYEncounterId); // 住院就诊id
            zYEncounterLocation.setLocationId(inHospitalRegisterDto.getWardLocationId()); // 病区id
            zYEncounterLocation.setFormEnum(LocationForm.WARD.getValue()); // 物理形式枚举
            iEncounterLocationService.save(zYEncounterLocation);
        }

        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"住院申请"}));
    }

    /**
     * 查询住院登记信息
     *
     * @param inHospitalRegisterQueryDto 查询dto
     * @param searchKey 模糊查询关键字
     * @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0
     * @param pageNo 当前页
     * @param pageSize 每页多少条
     * @param request 请求
     * @return 住院登记信息
     */
    @Override
    public IPage<InHospitalRegisterQueryDto> getRegisterInfo(InHospitalRegisterQueryDto inHospitalRegisterQueryDto,
        String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, HttpServletRequest request) {
        Integer encounterStatus = EncounterZyStatus.TO_BE_REGISTERED.getValue(); // 待登记
        // 构建查询条件
        QueryWrapper<InHospitalRegisterQueryDto> queryWrapper =
            HisQueryUtils.buildQueryWrapper(inHospitalRegisterQueryDto, searchKey,
                new HashSet<>(Arrays.asList("registrar", "source_name", "patient_name")), request);

        IPage<InHospitalRegisterQueryDto> inHospitalRegisterInfo = inHospitalRegisterAppMapper
            .getInHospitalRegisterInfo(new Page<>(pageNo, pageSize), EncounterClass.IMP.getValue(), encounterStatus,
                registeredFlag, LocationForm.WARD.getValue(), queryWrapper);
        inHospitalRegisterInfo.getRecords().forEach(e -> {
            // 性别
            e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
            // 计算年龄
            e.setAge(e.getBirthDate() != null ? AgeCalculatorUtil.getAge(e.getBirthDate()) : "");
        });

        return inHospitalRegisterInfo;
    }

    /**
     * 查询患者基本信息
     *
     * @param patientId 患者ID
     * @return 患者基本信息
     */
    @Override
    public InHospitalPatientInfoDto getPatientInfo(Long patientId) {
        InHospitalPatientInfoDto patientInfo = inHospitalRegisterAppMapper.getPatientInfo(patientId);
        // 性别
        patientInfo
            .setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, patientInfo.getGenderEnum()));
        // 计算年龄
        patientInfo
            .setAge(patientInfo.getBirthDate() != null ? AgeCalculatorUtil.getAge(patientInfo.getBirthDate()) : "");
        // 婚姻状态
        patientInfo.setMaritalStatusEnum_enumText(
            EnumUtils.getInfoByValue(MaritalStatus.class, patientInfo.getMaritalStatusEnum()));
        // 职业
        patientInfo.setPrfsEnum_enumText(EnumUtils.getInfoByValue(OccupationType.class, patientInfo.getPrfsEnum()));
        return patientInfo;
    }

    /**
     * 查询住院就诊信息
     *
     * @param encounterId 住院就诊id
     * @return 住院就诊信息
     */
    @Override
    public InHospitalInfoDto getInHospitalInfo(Long encounterId) {
        InHospitalInfoDto inHospitalInfo = inHospitalRegisterAppMapper.getInHospitalInfo(encounterId,
            LocationForm.WARD.getValue(), Whether.YES.getValue(), ParticipantType.ADMITTER.getCode(),
            AccountType.PERSONAL_CASH_ACCOUNT.getCode());
        // 优先级(患者病情)
        inHospitalInfo
            .setPriorityEnum_enumText(EnumUtils.getInfoByValue(PriorityLevel.class, inHospitalInfo.getPriorityEnum()));
        // 病区号
        String wardBusNo = inHospitalInfo.getWardBusNo();
        if (!StringUtils.isEmpty(wardBusNo)) {
            // 计算床位数
            Integer totalBedsNum = inHospitalRegisterAppMapper.getTotalBedsNum(LocationForm.BED.getValue(),
                LocationStatus.INACTIVE.getValue(), wardBusNo);
            inHospitalInfo.setTotalBedsNum(totalBedsNum); // 当前病区总床位数
            Integer idleBedsNum = inHospitalRegisterAppMapper.getIdleBedsNum(LocationForm.BED.getValue(),
                LocationStatus.IDLE.getValue(), wardBusNo);
            inHospitalInfo.setIdleBedsNum(idleBedsNum); // 当前病区空闲床位数
        }
        return inHospitalInfo;
    }

    /**
     * 根据病区号查询床位数
     *
     * @param wardBusNo 病区号
     * @return 床位数
     */
    @Override
    public HashMap<String, Integer> getBedsNumByWardBusNo(String wardBusNo) {
        HashMap<String, Integer> resMap = new HashMap<>();
        // 当前病区总床位数
        Integer totalBedsNum = inHospitalRegisterAppMapper.getTotalBedsNum(LocationForm.BED.getValue(),
            LocationStatus.INACTIVE.getValue(), wardBusNo);
        // // 当前病区空闲床位数
        Integer idleBedsNum = inHospitalRegisterAppMapper.getIdleBedsNum(LocationForm.BED.getValue(),
            LocationStatus.IDLE.getValue(), wardBusNo);
        resMap.put("totalBedsNum", totalBedsNum);
        resMap.put("idleBedsNum", idleBedsNum);
        return resMap;
    }

    /**
     * 更新患者信息
     *
     * @param patientUpdateDto 更新患者信息dto
     * @return 结果
     */
    @Override
    public R<?> updatePatient(PatientUpdateDto patientUpdateDto) {
        Patient patient = new Patient();
        patient.setId(patientUpdateDto.getPatientId());
        patient.setName(patientUpdateDto.getName()); // 患者姓名
        patient.setPyStr(ChineseConvertUtils.toPinyinFirstLetter(patientUpdateDto.getName())); // 拼音码
        patient.setWbStr(ChineseConvertUtils.toWBFirstLetter(patientUpdateDto.getName())); // 五笔码
        patient.setGenderEnum(patientUpdateDto.getGenderEnum()); // 性别编码
        patient.setBirthDate(patientUpdateDto.getBirthDate()); // 生日
        patient.setPhone(patientUpdateDto.getPhone()); // 电话
        patient.setIdCard(patientUpdateDto.getIdCard()); // 身份证号

        iPatientService.saveOrUpdate(patient);
        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"患者信息"}));
    }

    /**
     * 款台登记(门诊入口)
     *
     * @param inHospitalInfoDto 登记dto
     * @return 结果
     */
    @Override
    public R<?> registerByCashier(InHospitalInfoDto inHospitalInfoDto) {
        this.handleRegister(inHospitalInfoDto, null); // 处理入院登记信息
        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"住院登记"}));
    }

    /**
     * 无档登记
     *
     * @param noFilesRegisterDto 无档登记dto
     * @return 结果
     */
    @Override
    public R<?> noFilesRegister(NoFilesRegisterDto noFilesRegisterDto) {
        // 患者信息
        PatientInformationDto patientInformation = noFilesRegisterDto.getPatientInformation();
        // 新增患者
        R<?> r = patientInformationService.addPatient(patientInformation);
        if (r.getCode() == 200) {
            Patient patient = (Patient)r.getData();
            // 住院信息
            InHospitalInfoDto inHospitalInfoDto = noFilesRegisterDto.getInHospitalInfo();
            this.handleRegister(inHospitalInfoDto, patient); // 处理入院登记信息
        } else {
            R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00010, null));
        }

        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"无档登记"}));
    }

    /**
     * 处理入院登记信息
     * 
     * @param inHospitalInfoDto 住院信息dto
     * @param patient 患者信息
     */
    private void handleRegister(InHospitalInfoDto inHospitalInfoDto, Patient patient) {
        // 住院就诊id
        Long encounterId = inHospitalInfoDto.getEncounterId();

        // 处理住院就诊信息
        Encounter encounterReg = new Encounter();
        encounterReg.setId(encounterId);
        if (encounterId == null) {
            encounterReg.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.ADMISSION_NUM.getPrefix(), 4));// 住院编号
            encounterReg.setPatientId(patient.getId()); // 患者ID
            encounterReg.setStatusEnum(EncounterZyStatus.REGISTERED.getValue()); // 状态已登记
            encounterReg.setClassEnum(EncounterClass.IMP.getValue()); // 类别为住院
            encounterReg.setYbClassEnum(inHospitalInfoDto.getYbClassEnum()); // 医保类别
        }
        encounterReg.setStatusEnum(EncounterZyStatus.REGISTERED.getValue()); // 已登记
        encounterReg.setOrganizationId(inHospitalInfoDto.getInHospitalOrgId());// 住院科室id
        encounterReg.setPriorityEnum(inHospitalInfoDto.getPriorityEnum()); // 优先级(患者病情)
        encounterReg.setAdmitSourceCode(inHospitalInfoDto.getAdmitSourceCode()); // 入院类型
        encounterReg.setInWayCode(inHospitalInfoDto.getInWayCode()); // 入院方式
        encounterReg.setStartTime(inHospitalInfoDto.getStartTime()); // 入院日期
        encounterReg.setRegistrarId(SecurityUtils.getLoginUser().getPractitionerId()); // 登记员id
        iEncounterService.saveOrUpdate(encounterReg);

        // 先查询当前就诊是否已经分配了病区
        EncounterLocation encounterLocation = iEncounterLocationService.getOne(
            new LambdaQueryWrapper<EncounterLocation>().eq(EncounterLocation::getEncounterId, encounterReg.getId())
                .eq(EncounterLocation::getFormEnum, LocationForm.WARD.getValue()));
        // 处理病区信息
        EncounterLocation encounterLocationReg = new EncounterLocation();
        if (encounterLocation != null) {
            encounterLocationReg.setId(encounterLocation.getId());
        }
        encounterLocationReg.setEncounterId(encounterReg.getId()); // 住院就诊id
        encounterLocationReg.setLocationId(inHospitalInfoDto.getWardLocationId()); // 病区id
        encounterLocationReg.setFormEnum(LocationForm.WARD.getValue()); // 物理形式枚举
        iEncounterLocationService.saveOrUpdate(encounterLocationReg);

        // 处理账号预交金信息
        String contractNo = inHospitalInfoDto.getContractNo();
        // 自费标识
        boolean selfFundedFlag = CommonConstants.BusinessName.DEFAULT_CONTRACT_NO.equals(contractNo);
        // 生成个人现金账户的account记录用于存储预交金
        Account accountPersonalCash = new Account();
        accountPersonalCash.setTypeCode(AccountType.PERSONAL_CASH_ACCOUNT.getCode()); // 账户类型
        accountPersonalCash.setPatientId(
            inHospitalInfoDto.getPatientId() != null ? inHospitalInfoDto.getPatientId() : patient.getId()); // 患者id
        accountPersonalCash.setEncounterId(encounterReg.getId()); // 住院就诊id
        accountPersonalCash.setBalanceAmount(inHospitalInfoDto.getBalanceAmount()); // 账户余额
        // 自费
        if (selfFundedFlag) {
            accountPersonalCash.setEncounterFlag(Whether.YES.getValue());
        } else {
            // 生成非自费的账号
            Account accountNoSelfFunded = new Account();
            accountNoSelfFunded.setTypeCode(inHospitalInfoDto.getTypeCoce()); // 账户类型
            accountNoSelfFunded.setPatientId(
                inHospitalInfoDto.getPatientId() != null ? inHospitalInfoDto.getPatientId() : patient.getId()); // 患者id
            accountNoSelfFunded.setEncounterId(encounterReg.getId()); // 住院就诊id
            accountNoSelfFunded.setContractNo(contractNo); // 合同编码
            accountNoSelfFunded.setEncounterFlag(Whether.YES.getValue());
            iAccountService.save(accountNoSelfFunded);

            accountPersonalCash.setEncounterFlag(Whether.NO.getValue());
        }
        iAccountService.save(accountPersonalCash);

        // 保存支付信息
        String paymentNo = assignSeqUtil.getSeqByDay(AssignSeqEnum.PAYMENT_NO.getPrefix(), 20); // 生成流水号
        Date setlTime = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(setlTime);
        calendar.add(Calendar.HOUR_OF_DAY, 24);
        Date futureTime = calendar.getTime();

        // 支付管理主表
        PaymentReconciliation payment = new PaymentReconciliation();
        payment.setStatusEnum(PaymentStatus.SUCCESS.getValue()); // 状态
        payment.setPaymentNo(paymentNo); // 支付的业务标识符
        payment.setPaymentEnum(PaymentType.PAY.getValue()); // 付款类别
        payment.setPaymentReconciliationId(accountPersonalCash.getPatientId()); // 付款实体ID
        payment.setKindEnum(PaymentKind.HOSPITAL_DEPOSIT.getValue()); // 发起支付的工作流程类别-住院存款
        payment.setEntererId(SecurityUtils.getLoginUser().getPractitionerId()); // 收款员
        payment.setPatientId(accountPersonalCash.getPatientId()); // 患者ID
        payment.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId()); // 请求支付责任人ID
        payment.setOutcomeEnum(PaymentOutcome.PARTIAL.getCode()); // 付款结果
        payment.setLocationId(-99L); // 支付位置
        payment.setExpirationDate(futureTime); // 到期时间
        payment.setTenderedAmount(inHospitalInfoDto.getBalanceAmount()); // 应收金额
        payment.setDisplayAmount(inHospitalInfoDto.getBalanceAmount()); // 付款总额
        payment.setPrintCount(0); // 打印标识
        payment.setEncounterId(encounterReg.getId()); // 住院就诊id
        payment.setBillDate(setlTime); // 结算时间
        iPaymentReconciliationService.save(payment);

        // 支付管理子表
        PaymentRecDetail paymentDetail = new PaymentRecDetail();
        paymentDetail.setAccountId(accountPersonalCash.getId()); // 账户
        paymentDetail.setPayEnum(YbPayment.SELF_CASH_PAY.getValue()); // 支付类型
        paymentDetail.setPayLevelEnum(YbPayment.SELF_CASH_PAY.getLevel()); // 支付类型等级
        paymentDetail.setAmount(inHospitalInfoDto.getBalanceAmount()); // 金额
        paymentDetail.setPayTransNo(CommonConstants.BusinessName.DEFAULT_CONTRACT_NO);
        paymentDetail.setReconciliationId(payment.getId()); // 付款id

        iPaymentRecDetailService.save(paymentDetail);

        // 处理住院诊断(无档登记场景)
        if (patient != null && inHospitalInfoDto.getDiagnosisDefinitionId() != null) {
            // 住院诊断
            Condition zYCondition = new Condition();
            zYCondition.setVerificationStatusEnum(ConditionVerificationStatus.CONFIRMED.getValue()); // 验证状态
            zYCondition.setPatientId(patient.getId()); // 患者id
            zYCondition.setDefinitionId(inHospitalInfoDto.getDiagnosisDefinitionId()); // 诊断定义Id
            zYCondition.setYbNo(inHospitalInfoDto.getDiagnosisYbNo()); // 诊断对应的医保编码
            zYCondition.setRecordedDatetime(new Date()); // 记录时间
            zYCondition.setRecorderId(SecurityUtils.getLoginUser().getPractitionerId());// 记录人
            iConditionService.save(zYCondition);
            Long conditionId = zYCondition.getId();// 诊断id

            EncounterDiagnosis zYEncounterDiagnosis = new EncounterDiagnosis();
            zYEncounterDiagnosis.setEncounterId(encounterReg.getId()); // 住院就诊id
            zYEncounterDiagnosis.setConditionId(conditionId); // 诊断id
            zYEncounterDiagnosis.setMaindiseFlag(Whether.YES.getValue()); // 主诊断
            zYEncounterDiagnosis.setMedTypeCode(inHospitalInfoDto.getMedTypeCode());// 医疗类型
            zYEncounterDiagnosis.setDiagnosisDesc(inHospitalInfoDto.getDiagnosisDesc()); // 诊断描述
            zYEncounterDiagnosis.setIptDiseTypeCode(YbIptDiseTypeCode.ADMISSION_DIAGNOSIS.getValue()); // 住院患者疾病诊断类型代码
            iEncounterDiagnosisService.save(zYEncounterDiagnosis);
        }
        // 调用医保入院办理
        ybManager.hospitalAdmission();
    }

}
