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

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

import org.springframework.beans.factory.annotation.Autowired;
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.extension.plugins.pagination.Page;
import com.core.common.constant.HttpStatus;
import com.core.common.core.domain.R;
import com.core.common.exception.ServiceException;
import com.core.common.utils.DateUtils;
import com.core.common.utils.MessageUtils;
import com.core.common.utils.SecurityUtils;
import com.openhis.administration.domain.Practitioner;
import com.openhis.administration.domain.TraceNoManage;
import com.openhis.administration.service.ILocationService;
import com.openhis.administration.service.IPractitionerService;
import com.openhis.administration.service.ITraceNoManageService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.medication.domain.MedicationDispense;
import com.openhis.medication.service.IMedicationDispenseService;
import com.openhis.web.inhospitalnursestation.appservice.IMedicineSummaryAppService;
import com.openhis.web.pharmacymanage.appservice.ISummaryDispenseMedicineAppService;
import com.openhis.web.pharmacymanage.dto.MedicineSummaryDto;
import com.openhis.web.pharmacymanage.dto.MedicineSummaryInitDto;
import com.openhis.web.pharmacymanage.dto.MedicineSummarySearchParam;
import com.openhis.web.pharmacymanage.mapper.ReturnMedicineMapper;
import com.openhis.web.pharmacymanage.mapper.SummaryDispenseMedicineMapper;
import com.openhis.workflow.domain.DeviceDispense;
import com.openhis.workflow.domain.InventoryItem;
import com.openhis.workflow.domain.SupplyDelivery;
import com.openhis.workflow.domain.SupplyRequest;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IInventoryItemService;
import com.openhis.workflow.service.ISupplyDeliveryService;
import com.openhis.workflow.service.ISupplyRequestService;

/**
 * 住院汇总发放 应用实现类
 *
 * @author yuxj
 * @date 2025/6/3
 */
@Service
public class SummaryDispenseMedicineAppServiceImpl implements ISummaryDispenseMedicineAppService {

    @Autowired
    private IMedicationDispenseService medicationDispenseService;

    @Autowired
    private IDeviceDispenseService deviceDispenseService;

    @Autowired
    private IInventoryItemService iInventoryItemService;

    @Autowired
    private SummaryDispenseMedicineMapper inpatientMedicineDispenseMapper;

    @Autowired
    private ReturnMedicineMapper returnMedicineMapper;

    @Autowired
    private ILocationService locationService;
    @Resource
    private IPractitionerService practitionerService;
    @Resource
    private ISupplyRequestService supplyRequestService;
    @Resource
    private ISupplyDeliveryService supplyDeliveryService;
    @Autowired
    private ITraceNoManageService traceNoManageService;
    @Autowired
    private WesternMedicineDispenseAppServiceImpl westernMedicineDispenseAppServiceImpl;

    @Autowired
    private IMedicineSummaryAppService inpatientMedicineCollectionAppService;

    /**
     * 获取页面初始化信息
     *
     * @return 初始化信息
     */
    @Override
    public R<?> init() {

        MedicineSummaryInitDto initDto = new MedicineSummaryInitDto();

        // // 获取科室下拉选列表 todo 前台直接调用共通方法
        // List<Organization> organizationList = organizationService.getList(OrganizationType.DEPARTMENT.getValue(),
        // OrganizationClass.INPATIENT.getValue());
        // List<InpatientMedicineDispenseInitDto.LongOptions> organizationOptions = organizationList.stream().map(
        // organization -> new InpatientMedicineDispenseInitDto.LongOptions(organization.getId(),
        // organization.getName())).collect(Collectors.toList());

        // 未发药原因下拉选列表
        List<MedicineSummaryInitDto.IntegerOptions> notPerformedReasonOptions =
            Stream.of(NotPerformedReasonEnum.values())
                .map(notPerformedReason -> new MedicineSummaryInitDto.IntegerOptions(notPerformedReason.getValue(),
                    notPerformedReason.getInfo()))
                .collect(Collectors.toList());

        // 查询领药人列表
        List<Practitioner> applicantList = practitionerService.getList();

        // 发药状态
        List<MedicineSummaryInitDto.IntegerOptions> dispenseStatusOptions = new ArrayList<>();
        dispenseStatusOptions.add(new MedicineSummaryInitDto.IntegerOptions(DispenseStatus.SUMMARIZED.getValue(),
            DispenseStatus.SUMMARIZED.getInfo()));
        dispenseStatusOptions.add(new MedicineSummaryInitDto.IntegerOptions(DispenseStatus.COMPLETED.getValue(),
            DispenseStatus.COMPLETED.getInfo()));
        dispenseStatusOptions.add(new MedicineSummaryInitDto.IntegerOptions(DispenseStatus.DECLINED.getValue(),
            DispenseStatus.DECLINED.getInfo()));

        initDto// .setDepartmentOptions(organizationOptions)
            .setApplicantOptions(applicantList).setDispenseStatusOptions(dispenseStatusOptions)
            .setNotPerformedReasonOptions(notPerformedReasonOptions);
        return R.ok(initDto);
    }

    /**
     * 汇总医嘱查询
     *
     * @param searchParam 查询条件
     * @param searchKey 模糊查询关键字
     * @param pageNo 当前页码
     * @param pageSize 查询条数
     * @param request 请求数据
     */
    @Override
    public R<?> getMedicationSummaryInfo(MedicineSummarySearchParam searchParam, String searchKey, Integer pageNo,
        Integer pageSize, HttpServletRequest request) {

        // 构建查询条件
        QueryWrapper<MedicineSummarySearchParam> queryWrapper =
            HisQueryUtils.buildQueryWrapper(searchParam, searchKey, null, request);
        queryWrapper.orderByDesc(CommonConstants.FieldName.applyTime);

        // 查询医嘱详细信息
        Page<MedicineSummaryDto> prescriptionItemInfoPageDto =
            inpatientMedicineDispenseMapper.selectMedicationSummaryInfo(new Page<>(pageNo, pageSize), queryWrapper,
                CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
                SupplyType.DISPENSING_ORDER.getValue(), SupplyCategory.INPATIENT_PATIENT_SUMMARY_DISPENSING.getValue());

        List<MedicineSummaryDto.Option> unitList;
        // 个别项目设定
        for (MedicineSummaryDto prescriptionInfoDto : prescriptionItemInfoPageDto.getRecords()) {
            // 状态
            prescriptionInfoDto.setStatusEnum_enumText(
                EnumUtils.getInfoByValue(DispenseStatus.class, prescriptionInfoDto.getStatusEnum()));
            // 追溯码单位列表
            unitList = new ArrayList<>();
            unitList.add(new MedicineSummaryDto.Option(prescriptionInfoDto.getMaxUnitCode(),
                prescriptionInfoDto.getMaxUnitCode_dictText()));
            unitList.add(new MedicineSummaryDto.Option(prescriptionInfoDto.getMinUnitCode(),
                prescriptionInfoDto.getMinUnitCode_dictText()));

            prescriptionInfoDto.setUnitList(unitList);
        } ;
        return R.ok(prescriptionItemInfoPageDto);
    }

    /**
     * 处方单核对发药
     *
     * @param searchParam 条件
     * @return 处理结果
     */
    @Override
    public R<?> SummaryDispenseMedicine(List<MedicineSummarySearchParam> searchParam) {
        // 构建供应发放Id到追溯码的映射
        Map<Long, String> traceNoMap = new HashMap<>();
        for (MedicineSummarySearchParam param : searchParam) {
            if (param != null && param.getDeliveryId() != null) {
                traceNoMap.put(param.getDeliveryId(), param.getTraceNo());
            }
        }

        // 供应发放idList
        List<Long> deliveryIdList =
            searchParam.stream().map(MedicineSummarySearchParam::getDeliveryId).collect(Collectors.toList());
        // 获取供应发放信息列表
        List<SupplyDelivery> supplyDeliveryInfoList = supplyDeliveryService.selectByIdList(deliveryIdList);
        // 供应申请idList
        List<Long> requestIdList = supplyDeliveryInfoList.stream().map(SupplyDelivery::getRequestId).toList();
        // 耗材发放ID列表
        List<Long> devDispenseIdList = new ArrayList<>();
        // 药品发放ID列表
        List<Long> medDispenseIdList = new ArrayList<>();
        List<Long> idList;
        String[] idArray;

        // 构建项目发放Id到追溯码的映射
        Map<Long, String> medTraceNoMap = new HashMap<>();
        Map<Long, String> devTraceNoMap = new HashMap<>();

        for (SupplyDelivery item : supplyDeliveryInfoList) {
            // 按英文逗号分割字符串，得到数组
            idArray = item.getBasedOnIds().split(CommonConstants.Common.COMMA);
            // 流式处理：过滤空值、去除空格、转换为Long
            idList = Arrays.stream(idArray).map(String::trim) // 去除每个元素前后的空格
                .filter(str -> !str.isEmpty()) // 过滤空字符串（如连续逗号导致的空值）
                .map(Long::parseLong) // 转换为Long类型
                .toList(); // 收集为List<Long>
            // 设置发放ID列表，追溯码映射
            if (item.getBasedOnTable().equals(CommonConstants.TableName.MED_MEDICATION_DISPENSE)) {
                // 耗材发放ID列表
                devDispenseIdList.addAll(idList);
                // 追溯码映射
                for (Long id : idList) {
                    medTraceNoMap.put(id, traceNoMap.get(item.getId()));
                }
            } else {
                // 药品发放ID列表
                medDispenseIdList.addAll(idList);
                // 追溯码映射
                for (Long id : idList) {
                    devTraceNoMap.put(id, traceNoMap.get(item.getId()));
                }
            }
        }

        // 校验库存
        R<?> checkInventoryReturn =
            inpatientMedicineCollectionAppService.checkInventoryItem(devDispenseIdList, medDispenseIdList);
        if (checkInventoryReturn.getCode() != HttpStatus.SUCCESS) {
            return checkInventoryReturn;
        }
        // 库存信息
        List<InventoryItem> inventoryItemList = (List<InventoryItem>)checkInventoryReturn.getData();

        // todo 上面一行代码好使的话，就将下面注释的代码删除
        // // 检查数据类型是否为List<InventoryItem>
        // if (checkInventoryReturn.getData() instanceof List<?>) {
        // List<?> rawList = (List<?>)checkInventoryReturn.getData();
        //
        // // 进一步检查列表中的元素类型
        // boolean isInventoryItemList = true;
        // for (Object item : rawList) {
        // if (!(item instanceof InventoryItem)) {
        // isInventoryItemList = false;
        // break;
        // }
        // }
        //
        // if (isInventoryItemList) {
        // // 强制转换为List<InventoryItem>
        // inventoryItemList = (List<InventoryItem>)rawList;
        // } else {
        // // 处理类型不匹配的情况
        // return R.fail("校验库存列表中包含非InventoryItem类型的元素");
        // }
        // } else {
        // // 处理数据不是List的情况
        // return R.fail("校验库存返回的数据不是List类型");
        // }

        // 追溯码列表
        List<TraceNoManage> tracesNoManageList = new ArrayList<>();
        TraceNoManage traceNoManage;
        Date now = DateUtils.getNowDate();

        // 药品发药信息查询
        List<MedicationDispense> medDispenseList = medicationDispenseService
            .list(new LambdaQueryWrapper<MedicationDispense>().in(MedicationDispense::getId, medDispenseIdList));
        if (medDispenseList != null) {
            if (medDispenseList.stream().map(MedicationDispense::getStatusEnum)
                .anyMatch(x -> x.equals(DispenseStatus.COMPLETED.getValue()))) {
                throw new ServiceException("药品已发放，请勿重复发放");
            } else {
                for (MedicationDispense medicationDispense : medDispenseList) {
                    // 药品发放状态
                    medicationDispense.setStatusEnum(DispenseStatus.COMPLETED.getValue());
                    // 状态变更时间
                    medicationDispense.setStatusChangedTime(now);
                    // 发药数量
                    medicationDispense.setDispenseQuantity(medicationDispense.getQuantity());
                    // 发药时间
                    medicationDispense.setDispenseTime(now);
                    // 发药人
                    medicationDispense.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
                    // 药品追溯码
                    medicationDispense.setTraceNo(medTraceNoMap.get(medicationDispense.getId()));

                    // 使用逗号分割追溯码并转换为List
                    String[] traceNoList = medicationDispense.getTraceNo().split(CommonConstants.Common.COMMA);
                    for (String item : traceNoList) {
                        traceNoManage = new TraceNoManage();
                        // 追溯码处理
                        traceNoManage.setItemTable(CommonConstants.TableName.MED_MEDICATION_DEFINITION)
                            // 项目id
                            .setItemId(medicationDispense.getMedicationId())
                            // 仓库类型
                            .setLocationTypeEnum(LocationForm.PHARMACY.getValue())
                            // 仓库
                            .setLocationId(medicationDispense.getLocationId())
                            // 产品批号
                            .setLotNumber(medicationDispense.getLotNumber())
                            // 追溯码
                            .setTraceNo(item)
                            // 追溯码状态
                            .setStatusEnum(TraceNoStatus.OUT.getValue())
                            // 追溯码单位
                            .setUnitCode(medicationDispense.getUnitCode())
                            // 操作类型
                            .setOperationType(SupplyType.DISPENSE_MEDICATION.getValue());
                        tracesNoManageList.add(traceNoManage);
                    }
                }
                // 药品发放更新
                medicationDispenseService.updateBatchById(medDispenseList);
            }
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        // 耗材发药信息查询
        List<DeviceDispense> devDispenseList = deviceDispenseService
            .list(new LambdaQueryWrapper<DeviceDispense>().in(DeviceDispense::getId, devDispenseIdList));
        if (devDispenseList != null) {
            if (devDispenseList.stream().map(DeviceDispense::getStatusEnum)
                .anyMatch(x -> x.equals(DispenseStatus.COMPLETED.getValue()))) {
                throw new ServiceException("耗材已发放，请勿重复发放");
            } else {
                for (DeviceDispense deviceDispense : devDispenseList) {
                    // 药品发放状态
                    deviceDispense.setStatusEnum(DispenseStatus.COMPLETED.getValue());
                    // 发药数量
                    deviceDispense.setDispenseQuantity(deviceDispense.getQuantity());
                    // 发药时间
                    deviceDispense.setDispenseTime(now);
                    // 发药人
                    deviceDispense.setPerformerId(SecurityUtils.getLoginUser().getPractitionerId());
                    // 药品追溯码
                    deviceDispense.setTraceNo(devTraceNoMap.get(deviceDispense.getId()));
                    // todo 下面注释的代码是从耗材发放copy过来的，不知道要不要用
                    // // 发药人（长大写死发耗材人）
                    // deviceDispense.setPerformerId(CommonConstants.CCU.DisDeviceDoctor);
                    // // 根据数量设置追溯码
                    // deviceDispense.setTraceNo(String.join(CommonConstants.Common.COMMA,
                    // Collections.nCopies(deviceDispense.getQuantity(), CommonConstants.Common.DEV_TRACE_NO)));

                    // todo 耗材追溯码待定
                    // 使用逗号分割追溯码并转换为List
                    String[] traceNoList = deviceDispense.getTraceNo().split(CommonConstants.Common.COMMA);
                    for (String item : traceNoList) {
                        traceNoManage = new TraceNoManage();
                        // 追溯码处理
                        traceNoManage.setItemTable(CommonConstants.TableName.ADM_DEVICE_DEFINITION)
                            // 项目id
                            .setItemId(deviceDispense.getDeviceDefId())
                            // 仓库类型
                            .setLocationTypeEnum(LocationForm.WAREHOUSE.getValue())
                            // 仓库
                            .setLocationId(deviceDispense.getLocationId())
                            // 产品批号
                            .setLotNumber(deviceDispense.getLotNumber())
                            // 追溯码
                            .setTraceNo(item)
                            // 追溯码状态
                            .setStatusEnum(TraceNoStatus.OUT.getValue())
                            // 追溯码单位
                            .setUnitCode(deviceDispense.getUnitCode())
                            // 操作类型
                            .setOperationType(SupplyType.DISPENSE_MEDICATION.getValue());
                        tracesNoManageList.add(traceNoManage);
                    }
                }
                // 耗材发放更新
                deviceDispenseService.updateBatchById(devDispenseList);
            }
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        // 供应申请表更新
        supplyRequestService.updateCompletedStatusBatch(requestIdList);

        // 供应发放表更新
        supplyDeliveryService.updateCompletedStatusBatch(deliveryIdList);

        // 库存更新
        iInventoryItemService.updateBatchById(inventoryItemList);

        // 追溯码管理表数据追加
        boolean result = traceNoManageService.saveBatch(tracesNoManageList);
        if (!result) {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        // todo 医保进销存 不知道怎么改
        // 返回信息
        String returnMsg = null;
        // 调用医保商品销售接口
        String ybSwitch = SecurityUtils.getLoginUser().getOptionJson().getString(CommonConstants.Option.YB_SWITCH); // 医保开关
        if (Whether.YES.getCode().equals(ybSwitch)) {
            List<String> uploadFailedNoList =
                westernMedicineDispenseAppServiceImpl.ybMedicineIntegrated(medDispenseIdList, null);
            if (uploadFailedNoList != null) {
                returnMsg = "3505商品销售上传错误，错误项目编码" + uploadFailedNoList;
            } else {
                returnMsg = "3505商品销售上传成功";
            }
        }
        return R.ok(returnMsg, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null));
    }

    /**
     * 作废
     *
     * @param deliveryIdList 供应发放id
     * @param notPerformedReasonEnum 未发原因
     * @return 处理结果
     */
    @Override
    public R<?> dispenseCancel(List<Long> deliveryIdList, Integer notPerformedReasonEnum) {
        // 获取供应发放信息列表
        List<SupplyDelivery> supplyDeliveryInfoList = supplyDeliveryService.selectByIdList(deliveryIdList);
        List<SupplyRequest> supplyRequestInfoList;
        // 耗材发放ID列表
        List<Long> devDispenseIdList = new ArrayList<>();
        // 药品发放ID列表
        List<Long> medDispenseIdList = new ArrayList<>();
        List<Long> idList;
        String[] idArray;
        if (supplyDeliveryInfoList != null) {
            // 供应申请idList
            List<Long> requestIdList = supplyDeliveryInfoList.stream().map(SupplyDelivery::getRequestId).toList();
            // 获取供应发放信息列表
            supplyRequestInfoList = supplyRequestService.selectByIdList(requestIdList);
            for (SupplyDelivery item : supplyDeliveryInfoList) {
                // 按英文逗号分割字符串，得到数组
                idArray = item.getBasedOnIds().split(CommonConstants.Common.COMMA);
                // 流式处理：过滤空值、去除空格、转换为Long
                idList = Arrays.stream(idArray).map(String::trim) // 去除每个元素前后的空格
                    .filter(str -> !str.isEmpty()) // 过滤空字符串（如连续逗号导致的空值）
                    .map(Long::parseLong) // 转换为Long类型
                    .toList(); // 收集为List<Long>
                // 设置发放ID列表，追溯码映射
                if (item.getBasedOnTable().equals(CommonConstants.TableName.MED_MEDICATION_DISPENSE)) {
                    // 耗材发放ID列表
                    devDispenseIdList.addAll(idList);
                } else {
                    // 药品发放ID列表
                    medDispenseIdList.addAll(idList);
                }
            }
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        Date now = DateUtils.getNowDate();

        // 获取发药单id列表
        List<MedicationDispense> medDispenseInfoList = medicationDispenseService.selectByIdList(medDispenseIdList);
        if (medDispenseInfoList != null) {
            for (MedicationDispense medicationDispense : medDispenseInfoList) {
                // 药品发放状态
                medicationDispense.setStatusEnum(DispenseStatus.DECLINED.getValue());
                // 未发药原因
                medicationDispense.setNotPerformedReasonEnum(notPerformedReasonEnum);
                // 状态变更时间
                medicationDispense.setStatusChangedTime(now);
            }
            // 药品发放更新
            medicationDispenseService.updateBatchById(medDispenseInfoList);
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        // 获取发耗材单id列表
        List<DeviceDispense> devDispenseInfoList = deviceDispenseService.listByIds(devDispenseIdList);
        if (devDispenseInfoList != null) {
            for (DeviceDispense deviceDispense : devDispenseInfoList) {
                // 耗材发放状态
                deviceDispense.setStatusEnum(DispenseStatus.DECLINED.getValue());
                // 未发药原因
                deviceDispense.setNotPerformedReasonEnum(notPerformedReasonEnum);
            }
            // 耗材发放更新
            deviceDispenseService.updateBatchById(devDispenseInfoList);
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        // 更新供应发放信息
        for (SupplyDelivery deviceDispense : supplyDeliveryInfoList) {
            // 状态
            deviceDispense.setStatusEnum(DispenseStatus.DECLINED.getValue());
        }
        // 更新
        supplyDeliveryService.updateBatchById(supplyDeliveryInfoList);

        // 更新供应申请信息
        if (supplyRequestInfoList != null) {
            for (SupplyRequest deviceDispense : supplyRequestInfoList) {
                // 状态
                deviceDispense.setStatusEnum(DispenseStatus.DECLINED.getValue());
            }
            // 更新
            supplyRequestService.updateBatchById(supplyRequestInfoList);
        } else {
            return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null));
        }

        return R.ok(MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null));
    }

}
