package com.hdkj.lt.si.service.estimation.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.hdkj.fhzg.optimization.request.MaintOrgRequest;
import com.hdkj.lt.base.exception.ExceptionCast;
import com.hdkj.lt.core.bizms.modle.dto.StateEstimation;
import com.hdkj.lt.core.bizms.modle.po.DwdShbDsFeederBase;
import com.hdkj.lt.core.bizms.modle.po.FhzgFeederAutoSwitchMapping;
import com.hdkj.lt.core.bizms.modle.request.StateEstimationEquipRequest;
import com.hdkj.lt.core.bizms.modle.request.StateEstimationRequest;
import com.hdkj.lt.core.rest.service.IRemoteCallService;
import com.hdkj.lt.core.sys.service.DwdShbDsFeederBaseService;
import com.hdkj.lt.si.dao.FhzgFeederAutoSwitchMappingMapper;
import com.hdkj.lt.si.dao.FhzgStateEstimationFeederMapper;
import com.hdkj.lt.si.service.estimation.StateEstimationService;
import com.hdkj.lt.si.service.remote.impl.SateEstimationRemoteCallServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StateEstimationServiceImpl implements StateEstimationService {

    @Resource(type = SateEstimationRemoteCallServiceImpl.class)
    private IRemoteCallService remoteCallService;

    @Value("${remote.stateEstimation.queryPeriodByFeeder-uri}")
    private String periodByFeederUri;

    @Value("${remote.stateEstimation.queryPeriodByEquip-uri}")
    private String periodByEquipUri;

    @Value("${remote.stateEstimation.queryPeriodByFeeder-minutes:60}")
    private long periodMinutes;


    /**
     * 失败重试次数 (不含首次). 即总尝试次数 = retryCount + 1.
     * 失败定义: 抛异常 或 远程返回 code≠000000.
     */
    private static final int retryCount = 2;

    /**
     * 单批 psrIds 上限 (远程接口限制 100)
     * */
    private static final int batchSize = 50;
    @Autowired
    private FhzgFeederAutoSwitchMappingMapper fhzgFeederAutoSwitchMappingMapper;

    @Autowired
    private DwdShbDsFeederBaseService dwdShbDsFeederBaseService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired(required = false)
    @Qualifier("seQueryExecutor")
    private ExecutorService executor;

    private static final DateTimeFormatter POINT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final List<String> CONTAINER_CODE = Arrays.asList("feeder", "mvTrans", "energyConsumer", "pv", "segment", "switch");
    /** 96 个合法时刻列名白名单, 杜绝 ${pointColumn} 动态拼接的 SQL 注入风险. */
    private static final Set<String> POINT_COLUMNS;
    static {
        Set<String> cols = new LinkedHashSet<>(96);
        for (int h = 0; h < 24; h++) {
            for (int m = 0; m < 60; m += 15) {
                cols.add("point_time_" + String.format("%02d%02d", h, m));
            }
        }
        POINT_COLUMNS = Collections.unmodifiableSet(cols);
    }
    @Autowired
    private FhzgStateEstimationFeederMapper mapper;
    /** 专用写入 mapper: BigDecimal 以 plain 文本写入, 避免科学计数法. */
    private final ObjectMapper writeMapper = new ObjectMapper();

    @Override
    public StateEstimation queryByFeeder(StateEstimationRequest request) {
        try {
            Object data = remoteCallService.call(periodByFeederUri, HttpMethod.POST, request, Object.class);
            return JSON.parseObject(JSON.toJSONString(data), StateEstimation.class);
        } catch (Exception e) {
            e.printStackTrace();
            ExceptionCast.cast("调用状估接口失败");
        }
        return new StateEstimation();
    }

    @Override
    public StateEstimation queryPeriodByEquip(StateEstimationEquipRequest request) {
        request.setEndTime(convertTimeFormat(request.getEndTime()));
        request.setStartTime(convertTimeFormat(request.getStartTime()));
        try {
            Object data = remoteCallService.call(periodByEquipUri, HttpMethod.POST, request, Object.class);
            return JSON.parseObject(JSON.toJSONString(data), StateEstimation.class);
        } catch (Exception e) {
            e.printStackTrace();
            ExceptionCast.cast("调用状估接口失败");
        }
        return new StateEstimation();
    }

    /**
     * 获取存储状估数据到redis
     *
     * @param request
     * @return
     */
    @Override
    public StateEstimation queryByFeederSave(StateEstimationRequest request) {
        StateEstimation stateEstimation = queryByFeeder(request);
        //log.info(stateEstimation.toString());
        log.info("调用状估接口并存储");
//        redisTemplate.opsForValue()
//                .set(request.getPsrIds().get(0) + request.getStartTime() + "zg", stateEstimation, 30, TimeUnit.MINUTES);
        //失效时间暂时移除
        redisTemplate.opsForValue()
                .set(request.getPsrIds().get(0) + request.getStartTime() + "zg", stateEstimation);
        return stateEstimation;

    }

    /**
     * 获取存储状估数据到redis
     *
     * @param request
     * @return
     */
    @Override
    public StateEstimation querySave(StateEstimationRequest request, StateEstimation stateEstimation) {
        log.info(stateEstimation.toString());
        redisTemplate.opsForValue()
                .set(request.getPsrIds().get(0) + request.getStartTime() + "zg", stateEstimation, 180, TimeUnit.MINUTES);
        return stateEstimation;

    }
    @Override
    public List<StateEstimation> queryEquipByCounty(MaintOrgRequest request) {
        //根据区县查询线路
        List<DwdShbDsFeederBase> list = dwdShbDsFeederBaseService.list(new LambdaQueryWrapper<DwdShbDsFeederBase>()
                .eq(DwdShbDsFeederBase::getPsrState, "20").eq(DwdShbDsFeederBase::getPubPrivFlag, "0")
                .eq(DwdShbDsFeederBase::getMaintOrg, request.getMaintOrg()));
        List<String> feederIdsList = list.stream().map(DwdShbDsFeederBase::getPsrId).collect(Collectors.toList());

        // 分批 (≤ batchSize, 远程限制 100)
        List<List<String>> batches = partition(feederIdsList, batchSize);
//        log.info("[SE] feederIds 总量={},  批次数={}, 批大小={}", feederIdsList.size(), batches.size(), batchSize);
        //合并所有线路到集合中
        List<StateEstimation> mergedList = Lists.newArrayList();
        List<String> pointTime = Lists.newArrayList();
        //存放失败的线路id
        List<String> failFeederIds = Lists.newArrayList();
        // 并行调用 (有界线程池), 每批带重试, 任一批最终失败则整体失败
        StateEstimationRequest req = buildStateEstimationReq(request.getPointTime());
        for (List<String> batchFeederIds : batches) {
            List<CompletableFuture<StateEstimation>> futures = batchFeederIds.stream()
                    .map(id -> CompletableFuture.supplyAsync(() -> callWithRetry(id, req), executor))
                    .collect(Collectors.toList());

            for (int i = 0; i < futures.size(); i++) {
                CompletableFuture<StateEstimation> future = futures.get(i);
                String feederId = feederIdsList.get(i);
                try {
                    StateEstimation part = future.get(20L,TimeUnit.SECONDS);
                    if (pointTime.isEmpty()) {
                        pointTime = part.getPointTime();
                    }
                    List<String> feederList = part.getPeriodFeederSeResult().stream()
                            .map(StateEstimation.StateEstimationCommonResult::getPsrId)
                            .collect(Collectors.toList());
                    String feeder = feederList.isEmpty() ? null : feederList.get(0);
                    mergeToList(mergedList, part, feeder);
                } catch (CompletionException e) {
                    // 取消尚未完成的批次, 减少无效远程调用
                    futures.forEach(f -> f.cancel(true));
                    log.error("[SE] 查询状估失败: {}", e.getMessage(), e);
                    failFeederIds.add(feederId);
                    ExceptionCast.cast("调用状估接口失败");
                } catch (ExecutionException | InterruptedException | TimeoutException e) {
                    futures.forEach(f -> f.cancel(true));
                    failFeederIds.add(feederId);
                    ExceptionCast.cast("调用状估接口失败");
                }catch (Exception e) {
                    //未知错误
                    futures.forEach(f -> f.cancel(true));
                    failFeederIds.add(feederId);
                    ExceptionCast.cast("调用状估接口失败");
                }
            }
            log.info("当前批次处理完成，本批馈线数量：{},成功数量：{}，失败数量{}，失败线路ID：{}",
                    batchFeederIds.size(), mergedList.size(),failFeederIds.size(),failFeederIds);
        }
        log.info("调用状估接口执行完毕，总馈线数量：{},成功数量：{}，失败数量{}，失败线路ID：{}",
                feederIdsList.size(), mergedList.size(),failFeederIds.size(),failFeederIds);

        return mergedList;
    }

    private StateEstimationRequest buildStateEstimationReq(String pointTime){
        // 当前时间前一个小时
        ZoneId zone = ZoneId.of(ZoneId.systemDefault().getId());
        LocalDateTime now = LocalDateTime.now(zone);
        int slot = (now.getMinute() / 15) * 15;
        LocalDateTime floorNow = now.withMinute(slot).withSecond(0).withNano(0);
        LocalDateTime point = floorNow.minusMinutes(periodMinutes);
        String s = point.format(POINT_FMT);
        // 指定补断面时刻时优先使用传入时间（手动补历史断面）
        if (pointTime != null && !pointTime.isEmpty()) {
            s = pointTime;
        }
        StateEstimationRequest req = new StateEstimationRequest();
        req.setContainerCode(CONTAINER_CODE);
        req.setStartTime(s);
        req.setEndTime(s);
        return req;
    }
//    @Override
//    public StateEstimation queryEquipByCounty(StateEstimationRequest request) {
//        List<String> psrIds = request.getPsrIds();
//        // 分批 (≤ batchSize, 远程限制 100)
////        List<List<String>> batches = partition(request.getPsrIds(), batchSize);
////        log.info("[SE] feederIds 总量={},  批次数={}, 批大小={}", request.getPsrIds(), batches.size(), batchSize);
//
//        // 并行调用 (有界线程池), 每批带重试, 任一批最终失败则整体失败
//        List<CompletableFuture<StateEstimation>> futures = psrIds.stream()
//                .map(psrId -> CompletableFuture.supplyAsync(() -> processFeeder(psrId, request), executor))
//                .collect(Collectors.toList());
//        try {
//            StateEstimation merged = emptyResult();
////            List<String> pointTime = Lists.newArrayList();
////            for (CompletableFuture<StateEstimation> f : futures) {
////                // 任一批失败 -> 抛 CompletionException -> 整体失败
////                StateEstimation part = f.join();
////                if (pointTime.isEmpty()) {
////                    pointTime = part.getPointTime();
////                }
////                String feeder = part.getPeriodFeederSeResult().stream()
////                        .map(StateEstimation.StateEstimationCommonResult::getPsrId).collect(Collectors.toList()).get(0);
////                merge(merged, part,feeder);
////
////            }
////            merged.setPointTime(pointTime);
//            // 2. 解析返回的JSON数据
//            String str = StreamUtils.copyToString(new ClassPathResource("状估接口返回.txt").getInputStream(), StandardCharsets.UTF_8);
//            StateEstimation stateEstimation = JSON.parseObject(str, StateEstimation.class);
//            //入库
////            saveStateEstimation(merged,pointTime,request.getPsrIds());
//            saveStateEstimation(stateEstimation,stateEstimation.getPointTime(),request.getPsrIds());
//            return merged;
//        } catch (CompletionException e) {
//            // 取消尚未完成的批次, 减少无效远程调用
////            for (CompletableFuture<?> f : futures) {
////                f.cancel(true);
////            }
//            log.error("[SE] 查询状估失败: {}", e.getMessage(), e);
//            ExceptionCast.cast("调用状估接口失败");
//        }catch (IOException e) {
//            log.error("[SE] 查询状估失败: {}", e.getMessage(), e);
//            ExceptionCast.cast("调用状估接口失败");
//        }
//        return emptyResult();
//    }

    /**
     * 获取所有入参线路自动化开关信息
     * （入参需要线路和时间信息，时间取某一时刻即可）
     *
     * @param request
     */
    public List<FhzgFeederAutoSwitchMapping> saveAutoSwitch(StateEstimationRequest request) {
        Snowflake snowflake = IdUtil.getSnowflake(1,1);
        request.setIsAuto(true);
        request.setContainerCode(Collections.singletonList("switch"));
        StateEstimation stateEstimation = queryByFeeder(request);
        List<FhzgFeederAutoSwitchMapping> list = new ArrayList<>();
        if (stateEstimation != null) {
            list = new ArrayList<>(stateEstimation.getPeriodSwitchSeResult().stream().filter(a ->
                    a.getIsAuto().equals("true")
            ).map(a -> {
                FhzgFeederAutoSwitchMapping autoSwitchMapping = new FhzgFeederAutoSwitchMapping();
                autoSwitchMapping.setSwitchId(a.getPsrId()).setFeederId(a.getFeederId()).setId(snowflake.nextId())
                        .setCreator("auto").setIsAuto(1);
                return autoSwitchMapping;
            }).collect(Collectors.toMap(
                    b -> Arrays.asList(b.getFeederId(), b.getSwitchId()), Function.identity(),
                    (existing, replacement) -> existing
            )).values());
        }
        request.getPsrIds().forEach(i -> fhzgFeederAutoSwitchMappingMapper.delete(new LambdaUpdateWrapper<FhzgFeederAutoSwitchMapping>().eq(FhzgFeederAutoSwitchMapping::getFeederId, i)));
        list.forEach(i -> fhzgFeederAutoSwitchMappingMapper.insert(i));
        return list;
    }

    private String convertTimeFormat(String original) {
        DateTimeFormatter dateTimeFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime dateTime = LocalDateTime.parse(original, dateTimeFormat);
        return dateTime.format(dateTimeFormatter);
    }

    /**
     * 分批查询
     * @param list
     * @param size
     * @return
     * @param <T>
     */
    private static <T> List<List<T>> partition(List<T> list, int size) {
        if (size <= 0) {
            size = 100;
        }
        List<List<T>> parts = new ArrayList<>();
        for (int i = 0; i < list.size(); i += size) {
            parts.add(new ArrayList<>(list.subList(i, Math.min(i + size, list.size()))));
        }
        return parts;
    }

    /**
     * 带重试远程调用(单条线路)
     * @param single
     * @return
     */
    private StateEstimation callWithRetry(String single, StateEstimationRequest req) {
        int maxAttempts = retryCount + 1;
        Exception last = null;
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                req.setPsrIds(Collections.singletonList(single));
                StateEstimation resp = this.queryByFeeder(req);
                if (resp != null) {
                    return resp;
                }
            } catch (Exception e) {
                last = e;
                log.warn("[SE] 批次异常, 第{}/{}次: {}, psrIds ={}", attempt, maxAttempts, e.getMessage(), single);
            }
        }
        throw new RuntimeException("状估调用失败 (已重试 " + retryCount+ " 次), psrIds ="
                + single + ", 最后错误: " + (last == null ? "unknown" : last.getMessage()), last);
    }

    /**
     * 含入库查询
     * @param single
     * @param req
     * @return
     */
    private StateEstimation processFeeder(String single,StateEstimationRequest req) {
        int maxAttempts = retryCount + 1;
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                req.setPsrIds(Collections.singletonList(single));
                StateEstimation resp = this.queryByFeeder(req);
                if (resp != null) {
                    //入库
                    saveStateEstimation(resp,resp.getPointTime(),Collections.singletonList(single));
                    return resp;
                }
            } catch (Exception e) {
                ExceptionCast.cast("调用状估接口失败");
            }
        }
        return null;
    }


    private static void merge(StateEstimation dst,StateEstimation src,String feeder) {
        if (src == null) {
            return;
        }
        addAll(dst.getPeriodEquipResult(), src.getPeriodEquipResult());
        addAll(dst.getPeriodFeederSeResult(), src.getPeriodFeederSeResult());
        addAll(dst.getPeriodSwitchSeResult(), src.getPeriodSwitchSeResult().stream().peek(p->p.setFeederId(feeder)).collect(Collectors.toList()));
        //配变
        addAll(dst.getPeriodMVTransSeResult(), src.getPeriodMVTransSeResult());
        //光伏
        addAll(dst.getPeriodPVSeResult(), src.getPeriodPVSeResult());
        //线路段
        addAll(dst.getPeriodSegmentSeResult(), src.getPeriodSegmentSeResult());
        //用户
        addAll(dst.getPeriodEnergyConsumerSeResult(),src.getPeriodEnergyConsumerSeResult());
    }

    private static void mergeToList(List<StateEstimation> dst,StateEstimation src,String feeder) {
        if (!Objects.nonNull(src)) {
            return;
        }
        src.getPeriodSwitchSeResult().forEach(p->p.setFeederId(feeder));
        dst.add(src);
    }
    private static <T> void addAll(List<T> dst, List<T> src) {
        if (src != null && !src.isEmpty()) {
            dst.addAll(src);
        }
    }
    private static StateEstimation emptyResult() {
        StateEstimation r = new StateEstimation();
        //线路
        r.setPeriodFeederSeResult(new ArrayList<>());
        //设备
        r.setPeriodEquipResult(new ArrayList<>());
        //开关
        r.setPeriodSwitchSeResult(new ArrayList<>());
        //配变
        r.setPeriodMVTransSeResult(new ArrayList<>());
        //光伏
        r.setPeriodPVSeResult(new ArrayList<>());
        //线路段
        r.setPeriodSegmentSeResult(new ArrayList<>());
        //用户
        r.setPeriodEnergyConsumerSeResult(new ArrayList<>());
        return r;
    }

    public void saveStateEstimation(StateEstimation stateEstimation,List<String> mergedPointTime,List<String> feeders) {
        if(Objects.nonNull(stateEstimation)){
            // 5. 按设备类型分组
            Map<String, List<StateEstimation.StateEstimationCommonResult>> feederSeMap = Objects.nonNull(stateEstimation.getPeriodFeederSeResult())?
                    stateEstimation.getPeriodFeederSeResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationCommonResult::getPsrId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationTransResult>> transMap = Objects.nonNull(stateEstimation.getPeriodMVTransSeResult())?
                    stateEstimation.getPeriodMVTransSeResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationTransResult::getFeederId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationConsumerResult>> consumerMap = Objects.nonNull(stateEstimation.getPeriodEnergyConsumerSeResult())?
                    stateEstimation.getPeriodEnergyConsumerSeResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationConsumerResult::getFeederId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationCommonResult>> pvMap = Objects.nonNull(stateEstimation.getPeriodPVSeResult())?
                    stateEstimation.getPeriodPVSeResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationCommonResult::getFeederId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationSegmentResult>> segmentMap = Objects.nonNull(stateEstimation.getPeriodSegmentSeResult())?
                    stateEstimation.getPeriodSegmentSeResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationSegmentResult::getFeederId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationSwitchResult>> switchMap = Objects.nonNull(stateEstimation.getPeriodSwitchSeResult())?
                    stateEstimation.getPeriodSwitchSeResult().stream().filter(f->f.getFeederId()!=null).collect(Collectors.groupingBy(StateEstimation.StateEstimationSwitchResult::getFeederId)): Maps.newHashMap();
            Map<String, List<StateEstimation.StateEstimationCommonResult>> equipMap = Objects.nonNull(stateEstimation.getPeriodEquipResult())?
                    stateEstimation.getPeriodEquipResult().stream().collect(Collectors.groupingBy(StateEstimation.StateEstimationCommonResult::getFeederId)): Maps.newHashMap();
            // 6. 逐时刻: 取该索引单值组装快照并落库
            for (int i = 0; i < mergedPointTime.size(); i++) {
                String pointStr = mergedPointTime.get(i);
                LocalDateTime pt;
                try {
                    pt = LocalDateTime.parse(pointStr, POINT_FMT);
                } catch (DateTimeParseException e) {
                    log.warn("[SE-Collect] 无法解析时刻 '{}', 跳过", pointStr);
                    continue;
                }
                String dataDate = pt.toLocalDate().format(DATE_FMT);
                String pointColumn = "point_time_" + String.format("%02d%02d", pt.getHour(), pt.getMinute());
                if (!POINT_COLUMNS.contains(pointColumn)) {
                    log.warn("[SE-Collect] 时刻 '{}' 推导出非法列名 {}, 跳过", pointStr, pointColumn);
                    continue;
                }

                for (String feeder : feeders) {
                    if (feederSeMap.get(feeder) == null || feederSeMap.get(feeder).isEmpty()) {
                        // 该馈线该时刻无任何数据, 跳过(不建空行)
                        continue;
                    }
                    StateEstimation params = new StateEstimation();
                    params.setPointTime(Collections.singletonList(pointStr));
                    params.setPeriodFeederSeResult(feederSeMap.get(feeder));
                    params.setPeriodMVTransSeResult(transMap.get(feeder));
                    params.setPeriodEnergyConsumerSeResult(consumerMap.get(feeder));
                    params.setPeriodPVSeResult(pvMap.get(feeder));
                    params.setPeriodSegmentSeResult(segmentMap.get(feeder));
                    params.setPeriodSwitchSeResult(switchMap.get(feeder));
                    params.setPeriodEquipResult(equipMap.get(feeder));
                    String json;
                    try {
                        json = writeMapper.writeValueAsString(params);
                    } catch (JsonProcessingException e) {
                        log.warn("[SE-Collect] 馈线 {} 时刻 {} 序列化失败: {}", feeder, pointStr, e.getMessage());
                        continue;
                    }

                    try {
                        upsertPoint(feeder, dataDate, pointColumn, json);
                    } catch (Exception e) {
                        log.error("[SE-Collect] 馈线 {} 时刻 {} 落库失败: {}", feeder, pointStr, e.getMessage(), e);
                    }
                }
            }
        }
    }
    private void upsertPoint(String feeder, String dataDate, String pointColumn, String json) {
        Long id = mapper.selectIdByFeederAndDate(feeder, dataDate);
        if (id != null) {
            mapper.updatePointTime(feeder, dataDate, pointColumn, json);
        } else {
            mapper.insertPointTime(feeder, dataDate, pointColumn, json);
        }
    }
}
