package com.kym.service.cache;

import cn.hutool.extra.spring.SpringUtil;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.entity.admin.ConnectorInfo;
import com.kym.entity.admin.EquipmentRelation;
import com.kym.entity.admin.Station;
import com.kym.entity.common.RedisKeys;
import com.kym.service.admin.ConnectorInfoService;
import com.kym.service.admin.EquipmentRelationService;
import com.kym.service.admin.StationService;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author skyline
 * @description 缓存
 * @date 2023-08-25 22:58
 */
public enum KymCache {
    INSTANCE;


    private static ConcurrentHashMap<String, String> CONNECTOR_ID_SHORT_ID_MAPPING = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<String, String> SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME_MAPPING = new ConcurrentHashMap<>();


    /**
     * 获取枪头状态
     *
     * @param connectorId
     * @return
     */
    public Integer getConnectorStatus(String connectorId) {
        return Integer.valueOf(Objects.requireNonNull(KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_STATUS + connectorId)));
    }

    /**
     * 缓存枪头状态
     *
     * @param map
     */
    public void putConnectorId2Status(Map<String, Integer> map) {
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_STATUS + k, String.valueOf(v)));
    }


    /**
     * 缓存枪头短编号
     *
     * @param map
     */
    public void putConnectorId2ShortId(Map<String, String> map) {
        CONNECTOR_ID_SHORT_ID_MAPPING.putAll(map);
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_SHORT_ID + k, v));
    }

    /**
     * 通过设备编号或者枪头编号获取设备短编号
     *
     * @param id
     * @return
     */
    public String getShortIdByEquipmentIdOrConnectorId(String id) {
        var connectorId = getConnectorId(id);
        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_SHORT_ID + connectorId);
    }


    public void putConnectorId2StationId(Map<String, String> map) {
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_STATION_ID + k, v));
    }

    /**
     * 通过设备编号或者枪头编号获取站点id
     *
     * @param id
     * @return
     */
    public String getStationIdByEquipmentIdOrConnectorId(String id) {
        var connectorId = getConnectorId(id);
        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_STATION_ID + connectorId);
    }

    public void putStationId2Name(Map<String, String> map) {
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.STATION_ID_TO_NAME + k, v));
    }

    /**
     * 通过站点id获取站点名称
     *
     * @param stationId
     * @return
     */
    public String getStationNameById(String stationId) {
        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.STATION_ID_TO_NAME + stationId);
    }

    /**
     * 通过充电接口id获取站点名称
     *
     * @param connectorId
     * @return
     */
    public String getStationNameByConnectorId(String connectorId) {
        var stationId = getStationIdByEquipmentIdOrConnectorId(connectorId);
        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.STATION_ID_TO_NAME + stationId);
    }

    /**
     * 操作员对应有权限的站点
     *
     * @param map
     */
    public void putAdminUser2Stations(Map<Long, List<String>> map) {
        map.forEach((k, v) -> {
            String key = RedisKeys.ADMIN_USER_STATION_IDS + k;
            String[] values = v.toArray(new String[0]); // 优化数组创建方式，使用varargs来避免不必要的数组拷贝
            KymCacheInjector.redisTemplate.opsForSet().add(key, values);
        });
    }


    /**
     * 获取运营平台操作员有权限的站点ID
     *
     * @param adminUserId
     * @return
     */
    public List<String> getAdminUserStationIds(Long adminUserId) {
        return KymCacheInjector.redisTemplate.opsForSet().members(RedisKeys.ADMIN_USER_STATION_IDS + adminUserId).stream().toList();
    }

    public void putConnectorId2ParkingNo(Map<String, String> map) {
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_PARKING_NO + k, v));
    }

    /**
     * @param id
     * @return
     */
    public String getParkNoByEquipmentIdOrConnectorId(String id) {
        var connectorId = getConnectorId(id);
        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_PARKING_NO + connectorId);
    }

    /**
     * 将equipmentId,connectorId,shortId转化成17为的connectorId
     *
     * @param id
     * @return
     */
    public String getConnectorId(String id) {
        return switch (id.length()) {
            case 17 -> id;
            case 16 -> id.concat("1");
            case 6 ->
                    CONNECTOR_ID_SHORT_ID_MAPPING.entrySet().stream().filter(entry -> id.equals(entry.getValue())).map(Map.Entry::getKey).findFirst().get();
            default -> null;
        };
    }


    /**
     * 缓存短编号和设备信息
     *
     * @param map
     */
    public void putWashShortId2ProductKeyAndDeviceName(Map<String, String> map) {
        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.WASH_SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME + k, v));
        SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME_MAPPING.putAll(map);
    }

    /**
     * 通过短编号获取productKey和deviceName
     *
     * @param shortId
     * @return
     */
    public String[] getProductKeyAndDeviceNameByWashShortId(String shortId) {
        var res = new String[]{};
        var s = Objects.requireNonNull(KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.WASH_SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME + shortId));
        if (CommUtil.isNotEmptyAndNull(s)) {
            res = s.split(",");
        }
        if (res.length != 2) {
            throw new BusinessException("设备信息异常");
        }
        return res;
    }

    /**
     * 通过productKey和deviceName获取设备短编号
     */
    public String getShortIdByProductKeyAndDeviceName(String productKey, String deviceName) {
        return SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME_MAPPING.entrySet().stream().filter(entry -> (productKey+","+deviceName).equals(entry.getValue())).map(Map.Entry::getKey).findFirst().get();
    }


    @Component
    public static class KymCacheInjector implements ApplicationListener<ApplicationStartedEvent> {

        private static final StringRedisTemplate redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);

        private final EquipmentRelationService equipmentRelationService;
        private final StationService stationService;

        private final ConnectorInfoService connectorInfoService;


        private KymCacheInjector(EquipmentRelationService equipmentRelationService, StationService stationService, ConnectorInfoService connectorInfoService) {
            this.equipmentRelationService = equipmentRelationService;
            this.stationService = stationService;
            this.connectorInfoService = connectorInfoService;
        }

        @Override
        public void onApplicationEvent(ApplicationStartedEvent event) {
            // 将数据库数据缓存到redis
            var equipmentRelations = equipmentRelationService.list();

            var connectorId2ShortId = equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getShortId));
            KymCache.INSTANCE.putConnectorId2ShortId(connectorId2ShortId);

            var connector2Station = equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getStationId));
            KymCache.INSTANCE.putConnectorId2StationId(connector2Station);

            var stationId2Name = stationService.list().stream().collect(Collectors.toMap(Station::getStationId, Station::getStationName));
            KymCache.INSTANCE.putStationId2Name(stationId2Name);

            var connectorId2ParkingNo = equipmentRelations.stream().collect(Collectors.toMap(EquipmentRelation::getConnectorId, EquipmentRelation::getParkingNo));
            KymCache.INSTANCE.putConnectorId2ParkingNo(connectorId2ParkingNo);

            var connectorId2Status = connectorInfoService.list().stream().collect(Collectors.toMap(ConnectorInfo::getConnectorId, ConnectorInfo::getStatus));
            KymCache.INSTANCE.putConnectorId2Status(connectorId2Status);
        }
    }
}
