package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.vpp.domain.VppDevice;
import com.usky.vpp.domain.VppResourcePoint;
import com.usky.vpp.domain.VppSite;
import com.usky.vpp.mapper.VppDeviceMapper;
import com.usky.vpp.mapper.VppResourcePointMapper;
import com.usky.vpp.mapper.VppSiteMapper;
import com.usky.vpp.constant.VppTsdbConstants;
import com.usky.vpp.service.VppResourceBoardService;
import com.usky.vpp.service.VppTsdbQueryService;
import com.usky.vpp.service.vo.ResourceBoardDetailVO;
import com.usky.vpp.service.vo.ResourceBoardItemVO;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppResourceTypeHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class VppResourceBoardServiceImpl implements VppResourceBoardService {

    @Autowired
    private VppResourcePointMapper resourcePointMapper;
    @Autowired
    private VppSiteMapper siteMapper;
    @Autowired
    private VppDeviceMapper deviceMapper;
    @Autowired
    private VppTsdbQueryService vppTsdbQueryService;

    @Override
    public CommonPage<ResourceBoardItemVO> pageBoard(Map<String, Object> params) {
        List<VppResourcePoint> all = loadFilteredResources(params);
        List<ResourceBoardItemVO> items = buildBoardItems(all);
        items = filterByStatus(items, params);

        long pageNum = parseLong(params, "pageNum", 1L);
        long pageSize = parseLong(params, "pageSize", 10L);
        int from = (int) Math.min((pageNum - 1) * pageSize, items.size());
        int to = (int) Math.min(from + pageSize, items.size());
        List<ResourceBoardItemVO> pageRecords = from >= items.size()
                ? Collections.emptyList() : items.subList(from, to);
        return new CommonPage<>(pageRecords, (long) items.size(), pageNum, pageSize);
    }

    @Override
    public ResourceBoardDetailVO getBoardDetail(Long resourceId) {
        VppResourcePoint resource = resourcePointMapper.selectById(resourceId);
        if (resource == null || VppAuditHelper.isDeleted(resource.getDeleteFlag())) {
            throw new BusinessException("资源点不存在");
        }
        VppSite site = siteMapper.selectById(resource.getSiteId());
        VppDevice device = loadDeviceForResource(resource);
        Map<String, BigDecimal> tsdbMetrics = loadTsdbMetrics(device);
        ResourceBoardDetailVO detail = new ResourceBoardDetailVO();
        fillBoardItem(detail, resource, site, device, tsdbMetrics);
        detail.setDeviceId(resource.getDeviceId());
        detail.setDeviceName(device != null ? device.getDeviceName() : null);
        detail.setDeviceCode(device != null ? device.getDeviceCode() : null);
        detail.setIsControl(resource.getIsControl());
        detail.setIsSupportPeak(resource.getIsSupportPeak());
        detail.setIsSupportFm(resource.getIsSupportFm());
        detail.setResponseMin(resource.getResponseMin());
        if (site != null) {
            detail.setSiteAddress(site.getAddress());
            detail.setContactName(site.getContactName());
            detail.setContactPhone(site.getContactPhone());
        }
        if (Boolean.TRUE.equals(detail.getAbnormal())) {
            detail.setAbnormalReason(resolveAbnormalReason(tsdbMetrics));
        }
        detail.setDataTime(LocalDateTime.now());
        return detail;
    }

    private List<VppResourcePoint> loadFilteredResources(Map<String, Object> params) {
        LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<VppResourcePoint>()
                .eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(VppResourcePoint::getTenantId, SecurityUtils.getTenantId())
                .orderByDesc(VppResourcePoint::getCreateTime);
        if (params != null) {
            if (params.get("resourceType") != null) {
                wrapper.eq(VppResourcePoint::getResourceType, params.get("resourceType").toString());
            }
            if (params.get("siteId") != null) {
                wrapper.eq(VppResourcePoint::getSiteId, Long.parseLong(params.get("siteId").toString()));
            }
        }
        List<VppResourcePoint> resources = resourcePointMapper.selectList(wrapper);
        if (params == null) {
            return resources;
        }
        Set<Long> siteIds = resolveRegionSiteIds(params);
        if (siteIds != null) {
            resources = resources.stream()
                    .filter(r -> r.getSiteId() != null && siteIds.contains(r.getSiteId()))
                    .collect(Collectors.toList());
        }
        return resources;
    }

    private Set<Long> resolveRegionSiteIds(Map<String, Object> params) {
        boolean hasRegion = params.get("province") != null || params.get("city") != null || params.get("district") != null;
        if (!hasRegion) {
            return null;
        }
        LambdaQueryWrapper<VppSite> siteWrapper = new LambdaQueryWrapper<VppSite>()
                .eq(VppSite::getDeleteFlag, VppAuditHelper.NOT_DELETED);
        if (params.get("province") != null) {
            siteWrapper.eq(VppSite::getProvince, params.get("province").toString());
        }
        if (params.get("city") != null) {
            siteWrapper.eq(VppSite::getCity, params.get("city").toString());
        }
        if (params.get("district") != null) {
            siteWrapper.eq(VppSite::getDistrict, params.get("district").toString());
        }
        return siteMapper.selectList(siteWrapper).stream().map(VppSite::getId).collect(Collectors.toSet());
    }

    private List<ResourceBoardItemVO> buildBoardItems(List<VppResourcePoint> resources) {
        if (resources.isEmpty()) {
            return Collections.emptyList();
        }
        Set<Long> siteIds = resources.stream().map(VppResourcePoint::getSiteId).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> deviceIds = resources.stream().map(VppResourcePoint::getDeviceId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, VppSite> siteMap = siteIds.isEmpty() ? Collections.emptyMap()
                : siteMapper.selectBatchIds(siteIds).stream()
                .filter(s -> !VppAuditHelper.isDeleted(s.getDeleteFlag()))
                .collect(Collectors.toMap(VppSite::getId, s -> s, (a, b) -> a));
        Map<Long, VppDevice> deviceMap = deviceIds.isEmpty() ? Collections.emptyMap()
                : deviceMapper.selectBatchIds(deviceIds).stream()
                .filter(d -> !VppAuditHelper.isDeleted(d.getDeleteFlag()))
                .collect(Collectors.toMap(VppDevice::getId, d -> d, (a, b) -> a));

        Map<String, Map<String, BigDecimal>> tsdbMetricsMap = loadTsdbMetricsMap(deviceMap.values());

        List<ResourceBoardItemVO> items = new ArrayList<>();
        for (VppResourcePoint resource : resources) {
            VppDevice device = resolveDeviceForResource(resource, deviceMap);
            Map<String, BigDecimal> tsdbMetrics = resolveTsdbMetrics(device, tsdbMetricsMap);
            ResourceBoardItemVO item = new ResourceBoardItemVO();
            fillBoardItem(item, resource, siteMap.get(resource.getSiteId()), device, tsdbMetrics);
            items.add(item);
        }
        return items;
    }

    private VppDevice loadDeviceForResource(VppResourcePoint resource) {
        if (resource.getDeviceId() == null) {
            return null;
        }
        VppDevice device = deviceMapper.selectById(resource.getDeviceId());
        return matchesResourceSite(device, resource.getSiteId()) ? device : null;
    }

    private VppDevice resolveDeviceForResource(VppResourcePoint resource, Map<Long, VppDevice> deviceMap) {
        if (resource.getDeviceId() == null) {
            return null;
        }
        VppDevice device = deviceMap.get(resource.getDeviceId());
        return matchesResourceSite(device, resource.getSiteId()) ? device : null;
    }

    private boolean matchesResourceSite(VppDevice device, Long siteId) {
        return device != null && siteId != null && siteId.equals(device.getSiteId());
    }

    private void fillBoardItem(ResourceBoardItemVO item, VppResourcePoint resource, VppSite site, VppDevice device,
                               Map<String, BigDecimal> tsdbMetrics) {
        item.setResourceId(resource.getId());
        item.setResourceCode(resource.getResourceCode());
        item.setResourceName(resource.getResourceName());
        item.setResourceType(resource.getResourceType());
        item.setResourceTypeLabel(VppResourceTypeHelper.resourceTypeLabel(resource.getResourceType()));
        item.setSiteId(resource.getSiteId());
        item.setCapacityKw(resource.getCapacityKw());
        if (site != null) {
            item.setSiteName(site.getSiteName());
            item.setProvince(site.getProvince());
            item.setCity(site.getCity());
            item.setDistrict(site.getDistrict());
        }
        applyTsdbMetrics(item, tsdbMetrics, resource.getResourceType());
        // item.setSocPercent(VppResourceMockHelper.mockSocPercent(resource));
        // item.setEvcsChargePowerKw(VppResourceMockHelper.mockEvcsChargePowerKw(resource));
    }

    private void applyTsdbMetrics(ResourceBoardItemVO item, Map<String, BigDecimal> tsdbMetrics, String resourceType) {
        BigDecimal deviceStatus = getMetric(tsdbMetrics, "device_status");
        boolean abnormal = deviceStatus != null && deviceStatus.compareTo(BigDecimal.ZERO) == 0;
        item.setAbnormal(abnormal);
        item.setRunStatusLabel(resolveRunStatusLabel(deviceStatus));
        item.setRealtimePowerKw(getMetric(tsdbMetrics, VppTsdbConstants.METRIC_P));
        item.setVoltageV(getMetric(tsdbMetrics, "ua"));
        item.setCurrentA(getMetric(tsdbMetrics, "ia"));
        if ("ESS".equalsIgnoreCase(resourceType)) {
            item.setSocPercent(getMetric(tsdbMetrics, VppTsdbConstants.METRIC_SOC));
        }
        if ("EVCS".equalsIgnoreCase(resourceType)) {
            item.setEvcsChargePowerKw(getMetric(tsdbMetrics, VppTsdbConstants.METRIC_P));
        }
    }

    private Map<String, Map<String, BigDecimal>> loadTsdbMetricsMap(Collection<VppDevice> devices) {
        List<String> deviceUuids = devices.stream()
                .map(VppDevice::getDeviceUuid)
                .filter(StringUtils::hasText)
                .distinct()
                .collect(Collectors.toList());
        if (deviceUuids.isEmpty()) {
            return Collections.emptyMap();
        }
        return vppTsdbQueryService.queryLatestMetrics(deviceUuids, VppTsdbConstants.BOARD_REALTIME_METRICS);
    }

    private Map<String, BigDecimal> loadTsdbMetrics(VppDevice device) {
        if (device == null || !StringUtils.hasText(device.getDeviceUuid())) {
            return Collections.emptyMap();
        }
        Map<String, Map<String, BigDecimal>> metricsMap = vppTsdbQueryService.queryLatestMetrics(
                Collections.singletonList(device.getDeviceUuid()),
                VppTsdbConstants.BOARD_REALTIME_METRICS);
        return metricsMap.getOrDefault(device.getDeviceUuid(), Collections.emptyMap());
    }

    private Map<String, BigDecimal> resolveTsdbMetrics(VppDevice device, Map<String, Map<String, BigDecimal>> tsdbMetricsMap) {
        if (device == null || !StringUtils.hasText(device.getDeviceUuid())) {
            return Collections.emptyMap();
        }
        Map<String, BigDecimal> metrics = tsdbMetricsMap.get(device.getDeviceUuid());
        return metrics != null ? metrics : Collections.emptyMap();
    }

    private BigDecimal getMetric(Map<String, BigDecimal> tsdbMetrics, String metric) {
        if (CollectionUtils.isEmpty(tsdbMetrics)) {
            return null;
        }
        return tsdbMetrics.get(metric);
    }

    private String resolveRunStatusLabel(BigDecimal deviceStatus) {
        if (deviceStatus == null) {
            return "未知";
        }
        int status = deviceStatus.intValue();
        if (status == 0) {
            return "离线";
        }
        if (status == 1) {
            return "在线";
        }
        return "未知";
    }

    private String resolveAbnormalReason(Map<String, BigDecimal> tsdbMetrics) {
        BigDecimal deviceStatus = getMetric(tsdbMetrics, "device_status");
        if (deviceStatus != null && deviceStatus.compareTo(BigDecimal.ZERO) == 0) {
            return "设备通信离线";
        }
        return "设备运行异常";
    }

    private List<ResourceBoardItemVO> filterByStatus(List<ResourceBoardItemVO> items, Map<String, Object> params) {
        if (params == null || params.get("status") == null) {
            return items;
        }
        String status = params.get("status").toString();
        if ("abnormal".equalsIgnoreCase(status) || "1".equals(status)) {
            return items.stream().filter(i -> Boolean.TRUE.equals(i.getAbnormal())).collect(Collectors.toList());
        }
        if ("normal".equalsIgnoreCase(status) || "0".equals(status)) {
            return items.stream().filter(i -> !Boolean.TRUE.equals(i.getAbnormal())).collect(Collectors.toList());
        }
        return items;
    }

    private long parseLong(Map<String, Object> params, String key, long defaultValue) {
        if (params == null || params.get(key) == null) {
            return defaultValue;
        }
        return Long.parseLong(params.get(key).toString());
    }
}
