package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.vpp.client.VppUnClient;
import com.usky.vpp.config.VppUnProperties;
import com.usky.vpp.config.VppUnPropertiesRegistry;
import com.usky.vpp.config.VppUnTenantContext;
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.service.VppTsdbQueryService;
import com.usky.vpp.service.VppUnReportService;
import com.usky.vpp.constant.VppDrEventStatus;
import com.usky.vpp.domain.VppDrEvent;
import com.usky.vpp.mapper.VppDrEventMapper;
import com.usky.vpp.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class VppUnReportServiceImpl implements VppUnReportService {

    private static final Logger log = LoggerFactory.getLogger(VppUnReportServiceImpl.class);

    @Autowired
    private VppUnPropertiesRegistry propertiesRegistry;
    @Autowired
    private VppUnClient unClient;
    @Autowired
    private VppResourcePointMapper resourcePointMapper;
    @Autowired
    private VppSiteMapper siteMapper;
    @Autowired
    private VppDeviceMapper deviceMapper;
    @Autowired
    private VppSiteResourceHelper siteResourceHelper;
    @Autowired
    private VppTsdbQueryService vppTsdbQueryService;
    @Autowired
    private VppDrEventMapper drEventMapper;

    @Override
    public void handleCreateReportRequest(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        if (!properties.isOutboundActive()) {
            return;
        }
        String reportRequestId = resolveReportRequestId(createReportRequest);
        log.info("处理 UN CreateReportRequest reportRequestID={}", reportRequestId);

        Map<String, Object> ack = VppUnMessageBuilder.buildCreateReportResponse(createReportRequest, properties);
        unClient.post("CreateReportResponse", ack, true);

        if (!StringUtils.hasText(reportRequestId)) {
            return;
        }
        switch (reportRequestId) {
            case "MetaDataReport":
                doSubmitRegisterReport();
                break;
            case "IntervalDataReport":
                doSubmitIntervalDataReport(createReportRequest);
                break;
            case "MomentDataReport":
                doSubmitMomentDataReport(createReportRequest);
                break;
//            case "LoadForecastReport":
//                submitLoadForecastReport(createReportRequest);
//                break;
//            case "RegulateForecastReport":
//                submitRegulateForecastReport(createReportRequest);
//                break;
            default:
                log.info("暂不支持的报告类型: {}", reportRequestId);
                break;
        }
    }

    @Override
    public void submitRegisterReport() {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        if (!properties.isOutboundActive()) {
            return;
        }
        doSubmitRegisterReport();
    }

    private void doSubmitRegisterReport() {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        Integer tenantId = VppUnTenantContext.resolveTenantId();
        List<VppSite> sites = listActiveSites(tenantId);
        List<VppResourcePoint> resources = listActiveResources(true);
        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
        Map<String, Object> request = VppUnMessageBuilder.buildRegisterReportRequest(
                properties, sites, resources, deviceUnResourceIdMap);
        unClient.post("RegisterReportRequest", request, true);
        long siteMetricCount = sites.stream()
                .filter(site -> site != null && StringUtils.hasText(site.getUnResourceId()))
                .count();
        long resourceMetricCount = resources.stream()
                .filter(resource -> resource != null
                        && resource.getDeviceId() != null
                        && StringUtils.hasText(deviceUnResourceIdMap.get(resource.getDeviceId())))
                .count();
        log.info("已提交 RegisterReportRequest，虚拟电厂量测点={}，含负荷预测，站点量测点={}，资源点量测点={}",
                VppUnRegisterMetricDefinitions.VPP_REGISTER_METRICS.size(), siteMetricCount, resourceMetricCount);
    }

    @Override
    public void submitIntervalDataReport(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        if (!properties.isOutboundActive()) {
            return;
        }
        doSubmitIntervalDataReport(createReportRequest);
    }

    private void doSubmitIntervalDataReport(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        Integer tenantId = VppUnTenantContext.resolveTenantId();
        List<VppSite> sites = listActiveSites(tenantId);
        List<VppResourcePoint> resources = listActiveResources(false);
        List<VppResourcePoint> peakResources = listActiveResources(true);
        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
        Map<Long, VppDevice> deviceMap = loadDeviceMap(resources);
        Map<Long, List<String>> peakDeviceUuidsBySite = groupPeakDeviceUuidsBySite(peakResources, deviceMap);
        List<String> tenantPeakDeviceUuids = collectDeviceUuids(peakResources, deviceMap);

        LocalDate forecastDate = VppUnIntervalDataHelper.resolveForecastDate();
        boolean workday = VppBaselineHelper.isWorkdayResponse(forecastDate);
        int requiredCount = workday
                ? VppBaselineHelper.WEEKDAY_REFERENCE_COUNT
                : VppBaselineHelper.NON_WEEKDAY_REFERENCE_COUNT;
        List<LocalDate> referenceDates = VppBaselineHelper.selectReferenceDates(
                forecastDate, workday, loadResponseHistoryDates(), requiredCount, LocalDate.now());

        LocalDateTime reportTime = LocalDateTime.now();
        List<VppUnRegisterReportIndex.Entry> entries = VppUnRegisterReportIndex.buildIntervalDataEntries(
                properties, sites, resources, deviceUnResourceIdMap);
        List<Map<String, Object>> pointCurveData = new ArrayList<>(entries.size());
        for (VppUnRegisterReportIndex.Entry entry : entries) {
            List<String> deviceUuids = resolveMomentDataDeviceUuids(
                    entry, tenantPeakDeviceUuids, peakDeviceUuidsBySite, deviceMap);
            List<Map<String, Object>> curveValues = VppUnIntervalDataHelper.buildForecastCurveValues(
                    vppTsdbQueryService, deviceUuids, referenceDates, forecastDate, entry.getDataMetric());
            pointCurveData.add(VppUnMessageBuilder.buildIntervalCurveDataEntry(entry.getRid(), curveValues));
        }

        Map<String, Object> request = VppUnMessageBuilder.buildIntervalDataReportRequest(
                properties, createReportRequest, pointCurveData, reportTime);
        unClient.post("IntervalDataReportRequest", request, true);
        log.info("已提交 IntervalDataReportRequest，pointCurveData={}，预测日={}，参考日数={}",
                pointCurveData.size(), forecastDate, referenceDates.size());
    }

    @Override
    public void submitMomentDataReport() {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        if (!properties.isOutboundActive()) {
            return;
        }
        doSubmitMomentDataReport(null);
    }

    private void doSubmitMomentDataReport(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        Integer tenantId = VppUnTenantContext.resolveTenantId();
        List<VppSite> sites = listActiveSites(tenantId);
        List<VppResourcePoint> resources = listActiveResources(false);
        List<VppResourcePoint> peakResources = listActiveResources(true);
        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
        Map<Long, VppDevice> deviceMap = loadDeviceMap(resources);
        Map<Long, List<String>> peakDeviceUuidsBySite = groupPeakDeviceUuidsBySite(peakResources, deviceMap);
        List<String> tenantPeakDeviceUuids = collectDeviceUuids(peakResources, deviceMap);

        LocalDateTime windowEnd = VppUnMomentDataHelper.resolveWindowEnd(LocalDateTime.now());
        LocalDateTime windowStart = VppUnMomentDataHelper.resolveWindowStart(windowEnd);

        List<VppUnRegisterReportIndex.Entry> entries = VppUnRegisterReportIndex.buildMomentDataEntries(
                properties, sites, resources, deviceUnResourceIdMap);
        List<Map<String, Object>> pointData = new ArrayList<>(entries.size());
        for (VppUnRegisterReportIndex.Entry entry : entries) {
            List<String> deviceUuids = resolveMomentDataDeviceUuids(
                    entry, tenantPeakDeviceUuids, peakDeviceUuidsBySite, deviceMap);
            BigDecimal value = VppUnMomentDataHelper.aggregateMetricValue(
                    vppTsdbQueryService, deviceUuids, windowStart, windowEnd, entry.getDataMetric());
            pointData.add(VppUnMessageBuilder.buildMomentDataPoint(
                    entry.getRid(), value, windowEnd, "good"));
        }

        Map<String, Object> request = VppUnMessageBuilder.buildMomentDataReportRequest(
                properties, createReportRequest, pointData, windowEnd);
        unClient.post("MomentDataReportRequest", request, true);
        log.info("已提交 MomentDataReportRequest，pointData={}，窗口 {} ~ {}",
                pointData.size(), windowStart, windowEnd);
    }

    private void submitLoadForecastReport(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        List<VppResourcePoint> resources = listActiveResources(true);
        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
        Map<String, Object> request = VppUnMessageBuilder.buildLoadForecastReportRequest(
                createReportRequest, resources, siteMap, properties);
        unClient.post("LoadForecastReportRequest", request, true);
        log.info("已提交 LoadForecastReportRequest");
    }

    private void submitRegulateForecastReport(Map<String, Object> createReportRequest) {
        VppUnProperties properties = propertiesRegistry.getCurrent();
        List<VppResourcePoint> resources = listActiveResources(true);
        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
        Map<String, Object> request = VppUnMessageBuilder.buildRegulateForecastReportRequest(
                createReportRequest, resources, siteMap, properties);
        unClient.post("RegulateForecastReportRequest", request, true);
        log.info("已提交 RegulateForecastReportRequest");
    }

    private List<VppSite> listActiveSites(Integer tenantId) {
        return siteMapper.selectList(new LambdaQueryWrapper<VppSite>()
                .eq(VppSite::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(tenantId != null, VppSite::getTenantId, tenantId)
                .orderByAsc(VppSite::getId));
    }

    private List<VppResourcePoint> listActiveResources(boolean controllableOnly) {
        Integer tenantId = VppUnTenantContext.resolveTenantId();
        LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<VppResourcePoint>()
                .eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(tenantId != null, VppResourcePoint::getTenantId, tenantId)
                .orderByAsc(VppResourcePoint::getId);
        if (controllableOnly) {
            wrapper.eq(VppResourcePoint::getIsSupportPeak, 1);
        }
        return resourcePointMapper.selectList(wrapper);
    }

    private Map<Long, VppDevice> loadDeviceMap(List<VppResourcePoint> resources) {
        if (resources == null || resources.isEmpty()) {
            return Collections.emptyMap();
        }
        List<Long> deviceIds = resources.stream()
                .map(VppResourcePoint::getDeviceId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (deviceIds.isEmpty()) {
            return Collections.emptyMap();
        }
        return deviceMapper.selectBatchIds(deviceIds).stream()
                .filter(device -> device != null && !VppAuditHelper.isDeleted(device.getDeleteFlag()))
                .collect(Collectors.toMap(VppDevice::getId, device -> device, (a, b) -> a));
    }

    private Map<Long, List<String>> groupPeakDeviceUuidsBySite(List<VppResourcePoint> peakResources,
                                                                 Map<Long, VppDevice> deviceMap) {
        Map<Long, List<String>> grouped = new HashMap<>();
        if (peakResources == null || deviceMap == null) {
            return grouped;
        }
        for (VppResourcePoint resource : peakResources) {
            if (resource == null || resource.getSiteId() == null || resource.getDeviceId() == null) {
                continue;
            }
            VppDevice device = deviceMap.get(resource.getDeviceId());
            if (device == null || !StringUtils.hasText(device.getDeviceUuid())) {
                continue;
            }
            grouped.computeIfAbsent(resource.getSiteId(), key -> new ArrayList<>())
                    .add(device.getDeviceUuid().trim());
        }
        return grouped;
    }

    private List<String> collectDeviceUuids(List<VppResourcePoint> resources, Map<Long, VppDevice> deviceMap) {
        if (resources == null || deviceMap == null) {
            return Collections.emptyList();
        }
        Set<String> uuids = new LinkedHashSet<>();
        for (VppResourcePoint resource : resources) {
            if (resource == null || resource.getDeviceId() == null) {
                continue;
            }
            VppDevice device = deviceMap.get(resource.getDeviceId());
            if (device != null && StringUtils.hasText(device.getDeviceUuid())) {
                uuids.add(device.getDeviceUuid().trim());
            }
        }
        return new ArrayList<>(uuids);
    }

    private List<String> resolveMomentDataDeviceUuids(VppUnRegisterReportIndex.Entry entry,
                                                      List<String> tenantPeakDeviceUuids,
                                                      Map<Long, List<String>> peakDeviceUuidsBySite,
                                                      Map<Long, VppDevice> deviceMap) {
        if (entry.getScope() == VppUnRegisterReportIndex.Scope.VPP) {
            return tenantPeakDeviceUuids;
        }
        if (entry.getScope() == VppUnRegisterReportIndex.Scope.SITE && entry.getSite() != null) {
            return peakDeviceUuidsBySite.getOrDefault(entry.getSite().getId(), Collections.emptyList());
        }
        if (entry.getScope() == VppUnRegisterReportIndex.Scope.RESOURCE && entry.getResource() != null) {
            VppDevice device = deviceMap.get(entry.getResource().getDeviceId());
            if (device != null && StringUtils.hasText(device.getDeviceUuid())) {
                return Collections.singletonList(device.getDeviceUuid().trim());
            }
        }
        return Collections.emptyList();
    }

    private Map<Long, String> loadDeviceUnResourceIdMap(List<VppResourcePoint> resources) {
        if (resources == null || resources.isEmpty()) {
            return Collections.emptyMap();
        }
        List<Long> deviceIds = resources.stream()
                .map(VppResourcePoint::getDeviceId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (deviceIds.isEmpty()) {
            return Collections.emptyMap();
        }
        return deviceMapper.selectBatchIds(deviceIds).stream()
                .filter(device -> device != null
                        && !VppAuditHelper.isDeleted(device.getDeleteFlag())
                        && StringUtils.hasText(device.getUnResourceId()))
                .collect(Collectors.toMap(VppDevice::getId, VppDevice::getUnResourceId, (a, b) -> a));
    }

    private Set<LocalDate> loadResponseHistoryDates() {
        List<VppDrEvent> events = drEventMapper.selectList(
                new LambdaQueryWrapper<VppDrEvent>()
                        .eq(VppDrEvent::getEventStatus, VppDrEventStatus.ENDED)
                        .eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED));
        Set<LocalDate> dates = new LinkedHashSet<>();
        for (VppDrEvent event : events) {
            if (event.getStartTime() != null) {
                dates.add(event.getStartTime().toLocalDate());
            }
        }
        return dates;
    }

    @SuppressWarnings("unchecked")
    private String resolveReportRequestId(Map<String, Object> request) {
        String id = VppUnPayloadHelper.getString(request, "reportRequestID", "reportRequestId");
        if (StringUtils.hasText(id)) {
            return id;
        }
        Object reportRequest = request.get("reportRequest");
        if (reportRequest instanceof List && !((List<?>) reportRequest).isEmpty()) {
            Object first = ((List<?>) reportRequest).get(0);
            if (first instanceof Map) {
                return VppUnPayloadHelper.getString((Map<String, Object>) first,
                        "reportRequestID", "reportRequestId");
            }
        }
        return null;
    }
}
