package com.openhis.web.doctorstation.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.core.common.utils.AssignSeqUtil;
import com.core.common.utils.SecurityUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.openhis.administration.domain.ChargeItem;
import com.openhis.administration.service.IChargeItemService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.enums.*;
import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper;
import com.openhis.web.datadictionary.dto.ActivityChildJsonDto;
import com.openhis.web.doctorstation.appservice.IDoctorStationAdviceAppService;
import com.openhis.web.doctorstation.dto.*;
import com.openhis.web.doctorstation.mapper.DoctorStationAdviceAppMapper;
import com.openhis.web.inhospitalnursestation.dto.MedicationRequestUseExe;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IActivityDefinitionService;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IDeviceRequestService;
import com.openhis.workflow.service.IServiceRequestService;

/**
 * 医嘱工具类
 */
@Component
public class AdviceUtils {

    @Resource
    AssignSeqUtil assignSeqUtil;

    @Resource
    DoctorStationAdviceAppMapper doctorStationAdviceAppMapper;

    @Resource
    OutpatientRegistrationAppMapper outpatientRegistrationAppMapper;

    @Resource
    IActivityDefinitionService iActivityDefinitionService;

    @Resource
    IDeviceRequestService iDeviceRequestService;

    @Resource
    IServiceRequestService iServiceRequestService;

    @Resource
    IDeviceDispenseService iDeviceDispenseService;

    @Resource
    IChargeItemService iChargeItemService;

    @Resource
    IDoctorStationAdviceAppService iDoctorStationAdviceAppService;

    /**
     * 校验库存
     *
     * @param adviceSaveList 医嘱信息
     * @return 提示信息
     */
    public String checkInventory(List<AdviceSaveDto> adviceSaveList) {
        // 医嘱定义ID集合
        List<Long> adviceDefinitionIdList =
            adviceSaveList.stream().map(AdviceSaveDto::getAdviceDefinitionId).collect(Collectors.toList());
        // 医嘱库存集合
        List<AdviceInventoryDto> adviceInventoryList =
            doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
                CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
        // 待发放个数信息
        List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
            CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
            DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
        // 预减库存
        List<AdviceInventoryDto> adviceInventory =
            this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
        // 检查库存
        for (AdviceSaveDto saveDto : adviceSaveList) {
            boolean matched = false;
            for (AdviceInventoryDto inventoryDto : adviceInventory) {
                // 匹配条件：adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
                if (saveDto.getAdviceDefinitionId().equals(inventoryDto.getItemId())
                    && saveDto.getAdviceTableName().equals(inventoryDto.getItemTable())
                    && saveDto.getLocationId().equals(inventoryDto.getLocationId())
                    && saveDto.getLotNumber().equals(inventoryDto.getLotNumber())) {
                    matched = true;
                    // 检查库存是否充足
                    BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity();
                    BigDecimal chineseHerbsDoseQuantity = saveDto.getChineseHerbsDoseQuantity(); // 中药付数
                    // 中草药医嘱的情况
                    if (chineseHerbsDoseQuantity != null && chineseHerbsDoseQuantity.compareTo(BigDecimal.ZERO) > 0) {
                        minUnitQuantity = minUnitQuantity.multiply(chineseHerbsDoseQuantity);
                    }
                    if (minUnitQuantity.compareTo(inventoryDto.getQuantity()) > 0) {
                        return saveDto.getAdviceName() + "在" + inventoryDto.getLocationName() + "库存不足";
                    }
                    break;
                }
            }
            // 如果没有匹配到库存
            if (!matched) {
                return saveDto.getAdviceName() + "未匹配到库存信息";
            }
        }
        return null; // 校验通过
    }

    /**
     * 执行住院医嘱,校验药品库存
     *
     * @param medUseExeList 药品请求信息
     * @return 结果
     */
    public String checkExeMedInventory(List<MedicationRequestUseExe> medUseExeList) {
        // 医嘱定义id集合
        List<Long> adviceDefinitionIdList =
            medUseExeList.stream().map(MedicationRequestUseExe::getMedicationId).collect(Collectors.toList());
        // 医嘱库存集合
        List<AdviceInventoryDto> adviceInventoryList =
            doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
                CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
        // 待发放个数信息
        List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
            CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
            DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
        // 预减库存
        List<AdviceInventoryDto> adviceInventory =
            this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
        // 生命提示信息集合
        List<String> tipsList = new ArrayList<>();
        for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
            Optional<AdviceInventoryDto> matchedInventory = adviceInventory.stream()
                .filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
                    && CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
                    && medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
                    && medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber()))
                .findFirst();
            // 匹配到库存信息
            if (matchedInventory.isPresent()) {
                AdviceInventoryDto inventoryDto = matchedInventory.get();
                if ((medicationRequestUseExe.getExecuteTimesNum()
                    .multiply(medicationRequestUseExe.getMinUnitQuantity()))
                        .compareTo(inventoryDto.getQuantity()) > 0) {
                    tipsList
                        .add("【" + medicationRequestUseExe.getBusNo() + "】在" + inventoryDto.getLocationName() + "库存不足");
                }
            } else {
                tipsList.add("【" + medicationRequestUseExe.getBusNo() + "】未匹配到库存信息");
            }
        }
        if (!tipsList.isEmpty()) {
            // 返回库存不足提示信息
            return String.join(";", tipsList);
        }
        return null; // 校验通过
    }

    /**
     * 预减库存
     *
     * @param adviceInventoryList 库存表数据
     * @param adviceDraftInventoryList 待发放数据
     * @return 预减库存
     */
    public List<AdviceInventoryDto> subtractInventory(List<AdviceInventoryDto> adviceInventoryList,
        List<AdviceInventoryDto> adviceDraftInventoryList) {
        // 1. 将 draft 列表转换为 Map，合并重复键的 quantity
        Map<String,
            AdviceInventoryDto> draftMap = adviceDraftInventoryList.stream()
                .collect(Collectors.toMap(
                    draft -> String.format(CommonConstants.Common.SS_DD_FORMAT, draft.getItemTable(),
                        draft.getLotNumber(), draft.getItemId(), draft.getLocationId()),
                    draft -> draft, (existing, replacement) -> {
                        // 合并重复键的 quantity
                        BigDecimal existingQty =
                            existing.getQuantity() != null ? existing.getQuantity() : BigDecimal.ZERO;
                        BigDecimal replacementQty =
                            replacement.getQuantity() != null ? replacement.getQuantity() : BigDecimal.ZERO;
                        existing.setQuantity(existingQty.add(replacementQty));
                        return existing;
                    }));
        // 2. 遍历原始库存列表，匹配并扣减
        return adviceInventoryList.stream().map(inventory -> {
            String key = String.format(CommonConstants.Common.SS_DD_FORMAT, inventory.getItemTable(),
                inventory.getLotNumber(), inventory.getItemId(), inventory.getLocationId());
            AdviceInventoryDto draft = draftMap.get(key);
            if (draft != null) {
                BigDecimal draftQty = draft.getQuantity() != null ? draft.getQuantity() : BigDecimal.ZERO;
                BigDecimal newQty = inventory.getQuantity().subtract(draftQty);
                inventory.setQuantity(newQty.compareTo(BigDecimal.ZERO) >= 0 ? newQty : BigDecimal.ZERO);
            }
            return inventory;
        }).collect(Collectors.toList());
    }

    /**
     * 处理诊疗子项信息
     *
     * @param childrenJson 诊疗子项JSON
     * @param organizationId 患者挂号/住院入库对应的科室id
     * @param activityChildrenJsonParams 诊疗子项参数类
     */
    public void handleActivityChild(String childrenJson, Long organizationId,
        ActivityChildrenJsonParams activityChildrenJsonParams) {
        // 治疗类型 (长期/临时)
        Integer therapyEnum = activityChildrenJsonParams.getTherapyEnum();
        // 当前登录账号的科室id
        Long orgId = SecurityUtils.getLoginUser().getOrgId();
        ObjectMapper objectMapper = new ObjectMapper();
        // 注册 JavaTimeModule
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        try {
            // 将JSON字符串转换为List<ActivityChildJsonDto>
            List<ActivityChildJsonDto> activityChildJsonDtoList =
                objectMapper.readValue(childrenJson, new TypeReference<>() {});
            // 创建子项的诊疗请求
            Long adviceDefinitionId; // 诊疗医嘱定义ID
            BigDecimal quantity; // 子项请求数量
            List<ChargeItem> chargeItemList = new ArrayList<>(); // 子项账单集合
            ServiceRequest serviceRequest;
            ChargeItem chargeItem;
            AdviceBaseDto adviceBaseDto;
            // 当前时间
            Date curDate = new Date();
            for (ActivityChildJsonDto activityChildJsonDto : activityChildJsonDtoList) {
                adviceDefinitionId = activityChildJsonDto.getAdviceDefinitionId();
                quantity = activityChildJsonDto.getChildrenRequestNum();
                adviceBaseDto = new AdviceBaseDto();
                adviceBaseDto.setAdviceDefinitionId(adviceDefinitionId);
                // 对应的子项诊疗医嘱信息
                AdviceBaseDto activityAdviceBaseDto =
                    iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null, null, null, organizationId, 1,
                        1, Whether.NO.getValue(), List.of(1, 2, 3)).getRecords().get(0);
                if (activityAdviceBaseDto != null) {
                    // 费用定价
                    AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);
                    if (advicePriceDto != null) {
                        // 生成诊疗请求
                        serviceRequest = new ServiceRequest();
                        serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());// 请求状态
                        serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4));
                        serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
                        serviceRequest.setQuantity(quantity); // 请求数量
                        serviceRequest.setUnitCode(activityAdviceBaseDto.getUnitCode()); // 请求单位编码
                        serviceRequest.setCategoryEnum(Integer.valueOf(activityAdviceBaseDto.getCategoryCode())); // 请求类型
                        serviceRequest.setActivityId(activityAdviceBaseDto.getAdviceDefinitionId());// 诊疗定义id
                        serviceRequest.setPatientId(activityChildrenJsonParams.getPatientId()); // 患者
                        serviceRequest.setRequesterId(SecurityUtils.getLoginUser().getPractitionerId()); // 开方医生
                        serviceRequest.setEncounterId(activityChildrenJsonParams.getEncounterId()); // 就诊id
                        serviceRequest.setAuthoredTime(curDate); // 请求签发时间
                        serviceRequest.setOrgId(organizationId); // 执行科室
                        serviceRequest.setParentId(activityChildrenJsonParams.getParentId()); // 子项诊疗的父id

                        iServiceRequestService.save(serviceRequest);

                        // 临时医嘱
                        if (TherapyTimeType.TEMPORARY.getValue().equals(therapyEnum)) {
                            // 生成账单
                            chargeItem = new ChargeItem();
                            chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
                            chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
                            chargeItem
                                .setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(serviceRequest.getBusNo()));
                            chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
                            chargeItem.setPatientId(activityChildrenJsonParams.getPatientId()); // 患者
                            chargeItem.setContextEnum(ChargeItemContext.ACTIVITY.getValue()); // 类型
                            chargeItem.setEncounterId(activityChildrenJsonParams.getEncounterId()); // 就诊id
                            chargeItem.setDefinitionId(advicePriceDto.getDefinitionId()); // 费用定价ID
                            chargeItem.setEntererId(SecurityUtils.getLoginUser().getPractitionerId());// 开立人ID
                            chargeItem.setRequestingOrgId(orgId); // 开立科室
                            chargeItem.setEnteredDate(curDate); // 开立时间
                            chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);// 医疗服务类型
                            chargeItem.setServiceId(serviceRequest.getId()); // 医疗服务ID
                            chargeItem.setProductTable(activityAdviceBaseDto.getAdviceTableName());// 产品所在表
                            chargeItem.setProductId(activityAdviceBaseDto.getAdviceDefinitionId());// 收费项id
                            chargeItem.setAccountId(activityChildrenJsonParams.getAccountId());// 关联账户ID

                            chargeItem.setQuantityValue(quantity); // 数量
                            chargeItem.setQuantityUnit(activityAdviceBaseDto.getUnitCode()); // 单位
                            chargeItem.setUnitPrice(advicePriceDto.getPrice()); // 单价
                            // 计算总价,保留4位小数
                            BigDecimal qty = quantity;
                            chargeItem.setTotalPrice(
                                qty.multiply(advicePriceDto.getPrice()).setScale(6, RoundingMode.HALF_UP)); // 总价
                            chargeItemList.add(chargeItem);
                        }
                    }
                }
            }
            // 更新主项账单的 children_json 字段
            if (chargeItemList.size() > 0) {
                String chargeItemChildrenJson = objectMapper.writeValueAsString(chargeItemList);
                ChargeItem mainChargeItem = new ChargeItem();
                mainChargeItem.setId(activityChildrenJsonParams.getChargeItemId()); // 主键
                mainChargeItem.setChildrenJson(chargeItemChildrenJson); // 子项json
                iChargeItemService.updateById(mainChargeItem);
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

}
