package com.kym.service.admin.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.kym.common.annotation.DynamicCache;
import com.kym.common.cache.PlatformCache;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.GeodsyDistanceUtils;
import com.kym.common.utils.PlatformAesUtil;
import com.kym.common.utils.PlatformConvertUtil;
import com.kym.entity.admin.*;
import com.kym.entity.admin.queryParams.StationQueryParam;
import com.kym.entity.admin.vo.ConnectorInfoVo;
import com.kym.entity.admin.vo.DetailStationVo;
import com.kym.entity.admin.vo.SimpleStationVo;
import com.kym.entity.admin.vo.StationVo;
import com.kym.entity.common.PageBean;
import com.kym.entity.miniapp.vo.PlatformPolicyInfoVo;
import com.kym.entity.platform.PlatformStationStatsInfo;
import com.kym.entity.platform.PlatformStationStatusInfo;
import com.kym.entity.platform.response.PlatformResponse;
import com.kym.mapper.admin.StationMapper;
import com.kym.service.admin.*;
import com.kym.service.cache.KymCache;
import com.kym.service.mybatisplus.MyBaseServiceImpl;
import com.kym.service.platform.PlatformApi;
import com.kym.service.platform.PlatformApiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.kym.common.utils.PlatformAesUtil.buildPlatformParams;

/**
 * <p>
 * 充电站信息 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2023-08-12
 */
@Service
@DS("db-admin")
@Slf4j
public class StationServiceImpl extends MyBaseServiceImpl<StationMapper, Station> implements StationService {

    private final PlatformApiService platformApiService;
    private final EquipmentInfoService equipmentInfoService;
    private final ConnectorInfoService connectorInfoService;
    private final EquipmentRelationService equipmentRelationService;
    private final ActivityStationService activityStationService;
    private final ActivityService activityService;
    
    // 虚拟线程执行器（Java 21+）
    private final java.util.concurrent.ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();

    public StationServiceImpl(PlatformApiService platformApiService, EquipmentInfoService equipmentInfoService,
                              ConnectorInfoService connectorInfoService, EquipmentRelationService equipmentRelationService,
                              ActivityStationService activityStationService, @Lazy ActivityService activityService) {
        this.platformApiService = platformApiService;
        this.equipmentInfoService = equipmentInfoService;
        this.connectorInfoService = connectorInfoService;
        this.equipmentRelationService = equipmentRelationService;
        this.activityStationService = activityStationService;
        this.activityService = activityService;
    }


    @Override
    public List<Station> listStation(StationQueryParam params) {
        // 判断数据权限
        var adminStationIds = KymCache.INSTANCE.getAdminUserStationIds(StpUtil.getLoginIdAsLong());
        QueryWrapper<Station> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("station_status", Station.STATION_STATUS_正常使用);


        if (CommUtil.isNotEmptyAndNull(params.getStationId())) {
            Set<String> stationIdSets = new java.util.HashSet<>(Set.of(params.getStationId()));
            stationIdSets.addAll(adminStationIds);
            queryWrapper.in("station_id", stationIdSets);
        }
        if (CommUtil.isNotEmptyAndNull(params.getStationIdList())) {
            queryWrapper.in("station_id", params.getStationIdList());
        }

        if (CommUtil.isNotEmptyAndNull(adminStationIds)) {
            queryWrapper.in("station_id", adminStationIds);
        }
        queryWrapper.orderByAsc("station_id");
        return list(queryWrapper);
    }

    /**
     * 查询互联互通平台站点信息
     *
     * @param platformName
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public List<StationVo> queryPlatformStationInfo(String platformName, int pageNum, int pageSize, String... stationIds) {
        var param = """
                {
                    "PageNo":%d,
                    "PageSize":%d,
                    "StationIDs":[%s],
                    "LastQueryTime":""
                }
                """.formatted(pageNum, pageSize, Arrays.stream(stationIds).map(id -> "\"" + id + "\"").collect(Collectors.joining(",")));
        var response = platformApiService.platformPost(platformName, PlatformApi.PLATFORM_QUERY_STATION_INFO.getApi(platformName), buildPlatformParams(platformName, param));
        var enStations = parsePlatformResponseData(response, platformName);
        var stationList = enStations.getJSONArray("StationInfos").toJavaList(StationVo.class);
        // 我方station表数据
        var stations = list();
        stationList.forEach(station -> {
            station.getEquipmentInfos().forEach(enEquipmentInfo ->
                    enEquipmentInfo.setShortId(KymCache.INSTANCE.getShortIdByEquipmentIdOrConnectorId(enEquipmentInfo.getEquipmentId()))
                            .setParkingNo(KymCache.INSTANCE.getParkNoByEquipmentIdOrConnectorId(enEquipmentInfo.getEquipmentId())));
            var res = stations.stream().filter(item -> item.getStationId().equals(station.getStationId())).toList();
            if (!res.isEmpty()) {
                station.setPictures(res.get(0).getPictures());
            } else {
                station.setPictures(null);
            }
        });
        return stationList;
    }

    /**
     * 解析平台返回数据为JSONObject
     *
     * @param response
     * @param platformName
     * @return
     */
    JSONObject parsePlatformResponseData(PlatformResponse response, String platformName) {
        var platform = PlatformCache.INSTANCE.getPlatformByName(platformName);
        return JSONObject.parseObject(PlatformAesUtil.decrypt(platform.getDataSecret(), platform.getDataSecretIv(), response.getData()));
    }


    /**
     * 获取缓存的 站点ID：桩信息 映射
     *
     * @return
     */
    @DynamicCache(timeout = 15 * 60 * 1000L)
    @Override
    public Map<String, List<EquipmentInfo>> getCachedEquipmentMap(String... stationId) {
        var list = equipmentInfoService.lambdaQuery().eq(CommUtil.isNotEmptyAndNull(stationId), EquipmentInfo::getStationId, stationId[0]).list();
        return list.stream().collect(Collectors.groupingBy(EquipmentInfo::getStationId));
    }

    /**
     * 获取缓存的 桩ID：枪信息 映射
     *
     * @return
     */
    @DynamicCache(timeout = 15 * 60 * 1000L)
    @Override
    public Map<String, List<ConnectorInfo>> getCachedEquipmentConnectorMap(String... stationId) {
        var list = connectorInfoService.lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(stationId), ConnectorInfo::getStationId, stationId[0])
                .list();
        return list.stream().collect(Collectors.groupingBy(ConnectorInfo::getStationId));
    }

    /**
     * 获取缓存的 桩ID：枪信息 映射
     *
     * @return
     */
    @DynamicCache(timeout = 15 * 60 * 1000L)
    @Override
    public Map<String, List<ConnectorInfo>> getCachedEquipmentConnectorMap() {
        return connectorInfoService.list().stream().collect(Collectors.groupingBy(ConnectorInfo::getEquipmentId));
    }


    /**
     * APP首页地图接口，传入经纬度，按照距离由远到近返回站点列表
     *
     * @param params
     * @return
     */
    @Override
//    @DynamicCache  有参接口有bug,例如搜索接口，会缓存历史搜索的结果返回，与预期不符
    public PageBean<SimpleStationVo> listStationForApp(StationQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        // 站点列表
        LambdaQueryWrapper<Station> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Station::getStationId, Station::getStationName, Station::getAddress, Station::getLocation,
                Station::getStationStatus, Station::getParkingNum, Station::getElectricityFee, Station::getServiceFee, Station::getParkFee);
        queryWrapper.in(CommUtil.isNotEmptyAndNull(params.getStationIdList()), Station::getStationId, params.getStationIdList());
        var stationVoList = new ArrayList<SimpleStationVo>();
        var stationList = list(queryWrapper);
        for (var station : stationList) {
            var simpleStationVo = new SimpleStationVo();
            BeanUtils.copyProperties(station, simpleStationVo);
            // 计算距离，单位m
            simpleStationVo.setDistance(GeodsyDistanceUtils.getDistance(
                    station.getLocation().getDoubleValue("stationLng"),
                    station.getLocation().getDoubleValue("stationLat"),
                    params.getLongitude(),
                    params.getLatitude(),
                    2));
            // 站点费率
            var policyInfoList = KymCache.INSTANCE.getStationPolicyInfo(station.getStationId());
            CommUtil.asserts(policyInfoList != null, "站点费率信息为空");

            // 根据当前时间获取当前策略
            var now = LocalTime.now();
            if (policyInfoList.size() == 1) {
                simpleStationVo.setCurrentPolicyInfo(policyInfoList.get(0));
            } else {
                policyInfoList.stream()
                        .filter(policyInfo -> policyInfo.getStartTime().isBefore(now))
                        .max(Comparator.comparing(PlatformPolicyInfoVo::getStartTime))
                        .ifPresent(simpleStationVo::setCurrentPolicyInfo);
            }
            stationVoList.add(simpleStationVo);
        }
        // 按照距离由近到远排序
        stationVoList.sort(Comparator.comparing(SimpleStationVo::getDistance));

        // 计算站点空闲数量
        var stationIds = stationVoList.stream().map(SimpleStationVo::getStationId).toList();
        var stationConnectorCountMap = connectorInfoService.lambdaQuery()
                .in(ConnectorInfo::getStationId, stationIds)
                .eq(ConnectorInfo::getStatus, EquipmentInfo.SERVICE_STATUS_空闲)
                .list()
                .stream().collect(Collectors.groupingBy(ConnectorInfo::getStationId, Collectors.counting()));

        // 填充站点空闲数量
        stationVoList.forEach(stationVo -> stationVo.setAvailableParkingNum(stationConnectorCountMap.getOrDefault(stationVo.getStationId(), 0L).intValue()));

        // 查询正在进行中的活动和各站点正在进行中的活动 （限充值优惠和优惠券领取活动） todo 性能待优化
        var rechargeRightsActivityList = activityService.lambdaQuery().eq(Activity::getStatus, Activity.STATUS_进行中).eq(Activity::getDiscountType, Activity.DISCOUNT_TYPE_服务费折扣权益).list();
        var couponActivityList = activityService.getAvailableCouponActivities();
        // Java 21: 使用ArrayList替代Stream.concat提高可读性
        var activityList = new ArrayList<Activity>(rechargeRightsActivityList);
        activityList.addAll(couponActivityList);

        if (CommUtil.isNotEmptyAndNull(activityList)) {
            var station2ActivityListMap = activityStationService.lambdaQuery().eq(ActivityStation::getStatus, Activity.STATUS_进行中).list()
                    .stream().collect(Collectors.groupingBy(ActivityStation::getStationId));
            var station2ActivityList = new HashMap<String, List<Activity>>();
            station2ActivityListMap.forEach((k, v) -> station2ActivityList.put(k,
                    v.stream().map(item -> activityList.stream().filter(activity -> activity.getId().equals(item.getActivityId())).findFirst().orElse(null)).toList()));
            stationVoList.forEach(vo -> vo.setActivityList(station2ActivityList.get(vo.getStationId())));
        }

        return new PageBean<>(stationVoList);
    }

    @Override
    public DetailStationVo stationInfoByShortId(String shortId) {
        EquipmentRelation equipmentRel = equipmentRelationService.getByShortId(shortId);
        CommUtil.assertsNonNull(equipmentRel, "该设备信息有误");
        return stationInfo(equipmentRel.getStationId());
    }

    @Override
    public DetailStationVo stationInfoByConnectorId(String connectorId) {
        var cid = KymCache.INSTANCE.getConnectorId(connectorId);
        EquipmentRelation equipmentRel = equipmentRelationService.getByConnectorId(cid);
        CommUtil.assertsNonNull(equipmentRel, "该设备信息有误");
        return stationInfo(equipmentRel.getStationId());
    }

    @Override
    public DetailStationVo stationInfoByEquipmentId(String equipmentId) {
        EquipmentRelation equipmentRel = equipmentRelationService.getByEquipmentId(equipmentId);
        CommUtil.assertsNonNull(equipmentRel, "该设备信息有误");
        return stationInfo(equipmentRel.getStationId());
    }

    /**
     * 站点详情
     *
     * @param stationId
     */
    @Override
    public DetailStationVo stationInfo(String stationId) {
        var station = lambdaQuery().eq(Station::getStationId, stationId).oneOpt();
        return station.map(s -> {
            var vo = new DetailStationVo();
            BeanUtils.copyProperties(s, vo);
            // 填充桩信息
            var list = getCachedEquipmentConnectorMap(s.getStationId()).get(s.getStationId()).stream()
                    .map(connectorInfo -> {
                        ConnectorInfoVo v = new ConnectorInfoVo();
                        BeanUtils.copyProperties(connectorInfo, v);
                        v.setShortId(KymCache.INSTANCE.getShortIdByEquipmentIdOrConnectorId(connectorInfo.getConnectorId()));
                        return v;
                    })
                    .toList();
            vo.setConnectInfoList(list);
            return vo;
        }).orElse(null);
    }

    @Override
    public List<PlatformStationStatusInfo> stationStatus(String[] stationIds) {
        // 平台名称对应站点 ID 列表（一次遍历完成分组）
        Map<String, List<String>> platform2StationIds = new HashMap<>();
        for (String stationId : stationIds) {
            String platform = PlatformCache.INSTANCE.getPlatformNameByStationId(stationId);
            platform2StationIds.computeIfAbsent(platform, k -> new ArrayList<>()).add(stationId);
        }
    
        log.info("开始查询站点状态，站点数：{}, 平台数：{}", stationIds.length, platform2StationIds.size());
        long startTime = System.currentTimeMillis();
    
        // 使用虚拟线程并行请求各平台数据
        List<java.util.concurrent.Future<List<PlatformStationStatusInfo>>> futures = new ArrayList<>();
            
        platform2StationIds.forEach((platformName, stations) -> {
            java.util.concurrent.Future<List<PlatformStationStatusInfo>> future = virtualExecutor.submit(
                () -> fetchPlatformStatus(platformName, stations)
            );
            futures.add(future);
        });
    
        // 收集结果
        List<PlatformStationStatusInfo> res = new ArrayList<>();
        for (var future : futures) {
            try {
                res.addAll(future.get());
            } catch (Exception e) {
                Thread.currentThread().interrupt();
                log.error("获取平台站点状态失败", e);
                throw new BusinessException("查询站点状态失败：" + e.getMessage());
            }
        }
            
        long elapsed = System.currentTimeMillis() - startTime;
        log.info("站点状态查询完成，耗时：{}ms, 返回数据量：{}", elapsed, res.size());
            
        return res;
    }
    
    /**
     * 获取单个平台的站点状态（供虚拟线程调用）
     *
     * @param platformName 平台名称
     * @param stationIds   站点 ID 列表
     * @return 站点状态列表
     */
    private List<PlatformStationStatusInfo> fetchPlatformStatus(String platformName, List<String> stationIds) {
        var param = """
                {
                    "StationIDs":[%s]
                }
                """.formatted(stationIds.stream().map(id -> "\"" + id + "\"").collect(Collectors.joining(",")));
            
        var response = platformApiService.platformPost(
            platformName, 
            PlatformApi.PLATFORM_QUERY_STATION_STATUS.getApi(platformName), 
            buildPlatformParams(platformName, param)
        );
            
        var platform = PlatformCache.INSTANCE.getPlatformByName(platformName);
        var enStationStatus = JSONObject.parseObject(
            PlatformAesUtil.decrypt(platform.getDataSecret(), platform.getDataSecretIv(), response.getData())
        );
            
        return enStationStatus.getJSONArray("StationStatusInfos")
            .toJavaList(PlatformStationStatusInfo.class);
    }

    /**
     * 充电桩统计
     *
     * @param stationId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public PlatformStationStatsInfo stationStats(String stationId, String startTime, String endTime) {
        var param = """
                {
                    "StationID":"%s",
                    "StartTime":"%s",
                    "EndTime":"%s"
                }
                """.formatted(stationId, startTime, endTime);
        var platformName = PlatformCache.INSTANCE.getPlatformNameByStationId(stationId);
        var response = platformApiService.platformPost(platformName, PlatformApi.PLATFORM_QUERY_STATION_STATS.getApi(platformName), buildPlatformParams(platformName, param));
        var enStationStats = parsePlatformResponseData(response, platformName);
        return enStationStats.getJSONObject("StationStats").toJavaObject(PlatformStationStatsInfo.class);
    }


    /**
     * 拉取互联互通平台充电站信息数据并更新本地服务器数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pullEnStationInfos(String stationId) {
        // 指定的站点信息
        var platformName = PlatformCache.INSTANCE.getPlatformNameByStationId(stationId);

        // 这里改成查询单个站点，避免数据量过大
        var stationVo = queryPlatformStationInfo(platformName, 1, 1000, stationId)
                .stream().filter(vo -> stationId.equals(vo.getStationId())).findFirst().orElse(null);

        var station = new Station();
        var equipmentList = new ArrayList<EquipmentInfo>();
        var connectorList = new ArrayList<ConnectorInfo>();

        // 组装数据
        assert stationVo != null;
        if (!stationVo.getEquipmentInfos().isEmpty()) {
            stationVo.getEquipmentInfos().forEach(item -> {
                // 桩体
                var equipment = new EquipmentInfo()
                        .setStationId(stationVo.getStationId())
                        .setShortId(item.getShortId())
                        .setPower(item.getConnectorInfos().get(0).getPower());
                BeanUtils.copyProperties(item, equipment);
                equipmentList.add(equipment);
                // 枪
                var connectorInfos = item.getConnectorInfos();
                if (!CommUtil.isEmptyOrNull(connectorInfos)) {
                    connectorInfos.forEach(connector -> {
                        // 默认状态设置为空闲
                        var connectorInfo = new ConnectorInfo().setStatus(EquipmentInfo.SERVICE_STATUS_空闲);
                        BeanUtils.copyProperties(connector, connectorInfo);
                        connectorInfo.setConnectorId(PlatformConvertUtil.parse2LocalConnectorId(connector.getConnectorId()));
                        connectorList.add(connectorInfo);
                    });
                }
            });
            equipmentList.sort(Comparator.comparing(EquipmentInfo::getShortId));
        }
        BeanUtils.copyProperties(stationVo, station);

        DynamicDataSourceContextHolder.push("db-admin");
        // 更新站点信息
        updateByQueryWrapper(station, s -> new QueryWrapper<Station>().eq("station_id", station.getStationId()));

        // 更新设备信息
        equipmentInfoService.updateBatchByQueryWrapper(equipmentList, equipmentInfo ->
                new QueryWrapper<EquipmentInfo>().eq("equipment_id", equipmentInfo.getEquipmentId()));

        // 更新枪信息
        connectorInfoService.updateBatchByQueryWrapper(connectorList, connectorInfo ->
                new QueryWrapper<ConnectorInfo>().eq("connector_id", connectorInfo.getConnectorId()));

        DynamicDataSourceContextHolder.poll();

        // 缓存更新，其他数据在站点导入时更新 com.kym.service.admin.impl.StationServiceImpl.importStation
        KymCache.INSTANCE.putStationId2Name(Map.of(stationVo.getStationId(), stationVo.getStationName()));
        KymCache.INSTANCE.putConnectorId2Status(connectorList.stream().collect(Collectors.toMap(ConnectorInfo::getConnectorId, ConnectorInfo::getStatus)));

        PlatformCache.INSTANCE.putStationId2PlatformName(Map.of(stationVo.getStationId(), platformName));
        PlatformCache.INSTANCE.putConnectorId2PlatformName(connectorList.stream().collect(Collectors.toMap(ConnectorInfo::getConnectorId, connectorInfo -> platformName)));

    }


    @Override
    @Transactional
    public void modifyStation(Station station) {
        lambdaUpdate().set(!CommUtil.isEmptyOrNull(station.getPictures()), Station::getPictures, station.getPictures())
                .eq(Station::getStationId, station.getStationId()).update();
    }


    @Override
    @Transactional
    public void importStation(JSONObject data) {
        var fieldIndexes = data.getJSONArray("fieldIndexes");
        if (fieldIndexes.size() != 8) {
            throw new BusinessException("导入数据格式错误，请正确匹配对应数据列");
        }

        String dataList = data.getString("dataList");
        var equipmentRelations = JSON.parseArray(dataList, EquipmentRelation.class).stream().filter(item -> !CommUtil.isEmptyOrNull(item.getId())).toList();

        // t_station
        var stations = equipmentRelations.stream().filter(CommUtil.distinctByKey(EquipmentRelation::getStationId))
                .map(item -> Map.of("stationId", item.getStationId(), "stationName", item.getStationName())).toList();
        var stationList = stations.stream().map(map -> new Station().setStationId(map.get("stationId")).setStationName(map.get("stationName"))).toList();
        replaceBatch(stationList);

        // t_equipment_info
        var equipmentInfoList = equipmentRelations.stream().map(item -> {
            var equipmentInfo = new EquipmentInfo();
            BeanUtils.copyProperties(item, equipmentInfo, "id");
            return equipmentInfo;
        }).toList();
        equipmentInfoService.replaceBatch(equipmentInfoList);

        // t_connector_info
        var connectorInfoList = equipmentRelations.stream().map(item -> {
            var connectorInfo = new ConnectorInfo();
            BeanUtils.copyProperties(item, connectorInfo, "id");
            return connectorInfo;
        }).toList();
        connectorInfoService.replaceBatch(connectorInfoList);

        // t_equipment_relation
        equipmentRelationService.replaceBatch(equipmentRelations);

        // 缓存更新
        KymCache.INSTANCE.putStationId2Name(stations.stream().collect(Collectors.toMap(map -> map.get("stationId"), map -> map.get("stationName"))));
        KymCache.INSTANCE.putConnectorId2ShortId(equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getShortId)));
        KymCache.INSTANCE.putConnectorId2StationId(equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getStationId)));
        KymCache.INSTANCE.putConnectorId2ParkingNo(equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getParkingNo)));
        PlatformCache.INSTANCE.putConnectorId2PlatformName(equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getPlatformName)));
        PlatformCache.INSTANCE.putStationId2PlatformName(equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getStationId, EquipmentRelation::getPlatformName, (oldValue, newValue) -> oldValue)));

    }

}
