package com.kym.service.admin.impl;

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.QueryWrapper;
import com.kym.common.annotation.DynamicCache;
import com.kym.common.enums.EnPlusApi;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.AESUtil;
import com.kym.common.utils.CommUtil;
import com.kym.entity.admin.ConnectorInfo;
import com.kym.entity.admin.EquipmentInfo;
import com.kym.entity.admin.EquipmentRelation;
import com.kym.entity.admin.Station;
import com.kym.entity.admin.vo.ConnectorVo;
import com.kym.entity.admin.vo.StationVo;
import com.kym.entity.enplus.EnEquipmentInfo;
import com.kym.entity.enplus.EnStationStatsInfo;
import com.kym.entity.enplus.EnStationStatusInfo;
import com.kym.mapper.admin.StationMapper;
import com.kym.service.MyBaseServiceImpl;
import com.kym.service.admin.ConnectorInfoService;
import com.kym.service.admin.EquipmentInfoService;
import com.kym.service.admin.EquipmentRelationService;
import com.kym.service.admin.StationService;
import com.kym.service.cache.KymCache;
import com.kym.service.enplus.EnPlusService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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

    private final EnPlusService enPlusService;
    private final EquipmentInfoService equipmentInfoService;
    private final ConnectorInfoService connectorInfoService;
    private final EquipmentRelationService equipmentRelationService;

    public StationServiceImpl(EnPlusService enPlusService, EquipmentInfoService equipmentInfoService, ConnectorInfoService connectorInfoService, EquipmentRelationService equipmentRelationService) {
        this.enPlusService = enPlusService;
        this.equipmentInfoService = equipmentInfoService;
        this.connectorInfoService = connectorInfoService;
        this.equipmentRelationService = equipmentRelationService;
    }

    @PostConstruct
    private void init() {
        // 手动切换数据源
        DynamicDataSourceContextHolder.push("db-admin");
        KymCache.INSTANCE.putStationId2Name(list().stream().collect(Collectors.toMap(Station::getStationId, Station::getStationName)));
        DynamicDataSourceContextHolder.poll();
    }

    @Override
    @DynamicCache // 方法的返回结果加一层方法缓存，5分钟内不变
    public List<StationVo> queryStationInfo(int pageNum, int pageSize) {
        var param = """
                {
                    "PageNo":%d,
                    "PageSize":%d,
                    "LastQueryTime":""
                }
                """.formatted(pageNum, pageSize);
        var response = enPlusService.enPlusPost(EnPlusApi.EN_PLUS_QUERY_STATION_INFO.getApi(), enPlusService.buildParams(param));
        var enStations = JSONObject.parseObject(AESUtil.decrypt(response.getData()));
        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);
            }
        });

        stationList.forEach(vo -> vo.setEquipmentInfos(vo.getEquipmentInfos().stream().sorted(Comparator.comparing(EnEquipmentInfo::getShortId)).toList()));
        return stationList;

    }


    @Override
    public List<EnStationStatusInfo> stationStatus(String[] ids) {
        var param = """
                {
                    "StationIDs":["%s"]
                }
                """.formatted(String.join("\",\"", ids));
        var response = enPlusService.enPlusPost(EnPlusApi.EN_PLUS_QUERY_STATION_STATUS.getApi(), enPlusService.buildParams(param));
        var enStationStatus = JSONObject.parseObject(AESUtil.decrypt(response.getData()));
        return enStationStatus.getJSONArray("StationStatusInfos").toJavaList(EnStationStatusInfo.class);
    }


    /**
     * 充电桩统计
     *
     * @param stationId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public EnStationStatsInfo stationStats(String stationId, String startTime, String endTime) {
        var param = """
                {
                    "StationID":"%s",
                    "StartTime":"%s",
                    "EndTime":"%s"
                }
                """.formatted(stationId, startTime, endTime);
        var response = enPlusService.enPlusPost(EnPlusApi.EN_PLUS_QUERY_STATION_STATS.getApi(), enPlusService.buildParams(param));
        // TODO: 2023-08-12 包装成自己的数据格式
        var enStationStats = JSONObject.parseObject(AESUtil.decrypt(response.getData()));
        return enStationStats.getJSONObject("StationStats").toJavaObject(EnStationStatsInfo.class);
    }


    /**
     * 拉取EN+充电站信息数据并更新本地服务器数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pullEnStationInfos(String stationId) {
        // 指定的站点信息
        var stationVo = queryStationInfo(1, 1000).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();
                        BeanUtils.copyProperties(connector, connectorInfo);
                        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();

        // 将充电站相关数据写入KymCache
        KymCache.INSTANCE.putStationId2Name(Map.of(stationVo.getStationId(), stationVo.getStationName()));


    }


    @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 addStation(List<ConnectorVo> connectorVos) {
        // 1、组装t_station数据

        // 2、组装t_equipment_info数据
        var equipmentInfos = connectorVos.stream().map(item -> {
            var equipmentInfo = new EquipmentInfo();
            BeanUtils.copyProperties(item, equipmentInfo);
            return equipmentInfo;
        }).toList();
        equipmentInfoService.saveBatch(equipmentInfos);

        // 3、组装t_connector_info数据
        var connectorInfos = connectorVos.stream().map(item -> {
            var connectorInfo = new ConnectorInfo();
            BeanUtils.copyProperties(item, connectorInfo);
            return connectorInfo;
        }).toList();
        connectorInfoService.saveBatch(connectorInfos);

        // 4、组装t_equipment_relation数据
        var equipmentRelations = connectorVos.stream().map(item -> {
            var equipmentRelation = new EquipmentRelation();
            BeanUtils.copyProperties(item, equipmentRelation);
            return equipmentRelation;
        }).toList();
        equipmentRelationService.saveBatch(equipmentRelations);

        // 5、更新KymCache数据
        // TODO redis发布订阅？
    }

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

        String dataList = data.getString("dataList");
        var list = JSON.parseArray(dataList, EquipmentRelation.class);

        // t_station
        var stations = list.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();
        saveBatch(stationList);

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

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

        // t_equipment_relation
        equipmentRelationService.saveBatch(list);
    }

}
