package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.vpp.domain.VppDevice;
import com.usky.vpp.domain.VppDrEvaluation;
import com.usky.vpp.domain.VppDrEvent;
import com.usky.vpp.domain.VppDrParticipation;
import com.usky.vpp.domain.VppResourcePoint;
import com.usky.vpp.domain.VppSite;
import com.usky.vpp.mapper.VppDeviceMapper;
import com.usky.vpp.mapper.VppDrEvaluationMapper;
import com.usky.vpp.mapper.VppDrEventMapper;
import com.usky.vpp.mapper.VppDrParticipationMapper;
import com.usky.vpp.mapper.VppResourcePointMapper;
import com.usky.vpp.mapper.VppSiteMapper;
import com.usky.vpp.constant.VppTsdbConstants;
import com.usky.vpp.service.VppCapabilityEvalService;
import com.usky.vpp.service.VppTsdbQueryService;
import com.usky.vpp.service.vo.CapabilityCategoryStatVO;
import com.usky.vpp.service.vo.CapabilityEvalSummaryVO;
import com.usky.vpp.service.vo.CapabilityHistoryVO;
import com.usky.vpp.service.vo.CapabilityLoadCurveVO;
import com.usky.vpp.service.vo.CapabilityLoadPointVO;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppCapabilityEvalHelper;
import com.usky.vpp.util.VppPageHelper;
import com.usky.vpp.util.VppSiteResourceHelper;
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.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

@Service
public class VppCapabilityEvalServiceImpl implements VppCapabilityEvalService {

    private static final int PARTICIPATE_STATUS_ACCEPT = 1;
    private static final int BASELINE_DAYS_AGO = 7;
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Autowired
    private VppResourcePointMapper resourcePointMapper;
    @Autowired
    private VppSiteMapper siteMapper;
    @Autowired
    private VppDeviceMapper deviceMapper;
    @Autowired
    private VppDrEventMapper eventMapper;
    @Autowired
    private VppDrParticipationMapper participationMapper;
    @Autowired
    private VppDrEvaluationMapper evaluationMapper;
    @Autowired
    private VppSiteResourceHelper siteResourceHelper;
    @Autowired
    private VppTsdbQueryService vppTsdbQueryService;

    @Override
    public CapabilityEvalSummaryVO getSummary(Long siteId) {
        VppSite site = resolveSite(siteId);
        List<VppResourcePoint> resources = listResources(siteId);
        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
        List<VppDevice> devices = loadDevices(resources);

        CapabilityEvalSummaryVO summary = new CapabilityEvalSummaryVO();
        summary.setSiteId(siteId);
        summary.setSiteName(site != null ? site.getSiteName() : "全部站点");
        summary.setTotal(buildCategoryStat(VppCapabilityEvalHelper.CATEGORY_TOTAL, resources, siteMap));
        summary.setLoad(buildCategoryStat(VppCapabilityEvalHelper.CATEGORY_LOAD, resources, siteMap));
        summary.setStorage(buildCategoryStat(VppCapabilityEvalHelper.CATEGORY_STORAGE, resources, siteMap));
        summary.setGeneration(buildCategoryStat(VppCapabilityEvalHelper.CATEGORY_GENERATION, resources, siteMap));

        BigDecimal maxUp = BigDecimal.ZERO;
        BigDecimal maxDown = BigDecimal.ZERO;
        for (VppResourcePoint resource : resources) {
            if (resource.getIsControl() == null || resource.getIsControl() != 1) {
                continue;
            }
            maxUp = maxUp.add(resource.getMaxUpKw() != null ? resource.getMaxUpKw() : BigDecimal.ZERO);
            maxDown = maxDown.add(resource.getMinDownKw() != null ? resource.getMinDownKw() : BigDecimal.ZERO);
        }
        applyRealtimeAdjustCapacity(resources, devices, summary, maxUp, maxDown);
        return summary;
    }

    private void applyRealtimeAdjustCapacity(List<VppResourcePoint> resources,
                                             List<VppDevice> devices,
                                             CapabilityEvalSummaryVO summary,
                                             BigDecimal fallbackUp,
                                             BigDecimal fallbackDown) {
        List<String> deviceUuids = resolveDeviceUuids(resources, devices);
        if (deviceUuids.isEmpty()) {
            summary.setMaxUpKw(fallbackUp);
            summary.setMaxDownKw(fallbackDown);
            return;
        }

        List<String> metrics = new ArrayList<>();
        metrics.addAll(VppTsdbConstants.UP_CAPACITY_METRICS);
        metrics.addAll(VppTsdbConstants.DOWN_CAPACITY_METRICS);
        Map<String, Map<String, BigDecimal>> latest = vppTsdbQueryService.queryLatestMetrics(deviceUuids, metrics);

        BigDecimal realtimeUp = VppCapabilityEvalHelper.sumLatestMetric(latest, VppTsdbConstants.UP_CAPACITY_METRICS);
        BigDecimal realtimeDown = VppCapabilityEvalHelper.sumLatestMetric(latest, VppTsdbConstants.DOWN_CAPACITY_METRICS);
        summary.setMaxUpKw(realtimeUp.compareTo(BigDecimal.ZERO) > 0 ? realtimeUp : fallbackUp);
        summary.setMaxDownKw(realtimeDown.compareTo(BigDecimal.ZERO) > 0 ? realtimeDown : fallbackDown);
    }

    @Override
    public CapabilityLoadCurveVO getLoadCurve(Long siteId, String date) {
        VppSite site = resolveSite(siteId);
        LocalDate statDate = parseDate(date);
        List<VppResourcePoint> resources = listResources(siteId);
        List<VppDevice> devices = loadDevices(resources);

        CapabilityLoadCurveVO curve = new CapabilityLoadCurveVO();
        curve.setSiteId(siteId);
        curve.setSiteName(site != null ? site.getSiteName() : "全部站点");
        curve.setDate(statDate.format(DATE_FMT));
        LoadCurveBuildResult buildResult = buildLoadCurvePoints(resources, devices, statDate);
        curve.setPoints(buildResult.points);
        curve.setDataSource(buildResult.dataSource);
        return curve;
    }

    private static class LoadCurveBuildResult {
        private final List<CapabilityLoadPointVO> points;
        private final String dataSource;

        private LoadCurveBuildResult(List<CapabilityLoadPointVO> points, String dataSource) {
            this.points = points;
            this.dataSource = dataSource;
        }
    }

    private LoadCurveBuildResult buildLoadCurvePoints(List<VppResourcePoint> resources,
                                                      List<VppDevice> devices,
                                                      LocalDate statDate) {
        List<String> deviceUuids = resolveDeviceUuids(resources, devices);
        if (!deviceUuids.isEmpty()) {
            LocalDateTime dayStart = statDate.atStartOfDay();
            LocalDateTime dayEnd = statDate.atTime(23, 59, 59);
            LocalDate baselineDate = statDate.minusDays(BASELINE_DAYS_AGO);
            LocalDateTime baselineStart = baselineDate.atStartOfDay();
            LocalDateTime baselineEnd = baselineDate.atTime(23, 59, 59);

            List<String> powerMetrics = new ArrayList<>(VppTsdbConstants.ACTIVE_POWER_METRICS);
            Map<String, Map<String, TreeMap<LocalDateTime, BigDecimal>>> actualData =
                    vppTsdbQueryService.queryDeviceMetricHistory(deviceUuids, dayStart, dayEnd, powerMetrics);
            if (VppCapabilityEvalHelper.hasTsdbPowerData(actualData, powerMetrics)) {
                Map<String, Map<String, TreeMap<LocalDateTime, BigDecimal>>> baselineData =
                        vppTsdbQueryService.queryDeviceMetricHistory(deviceUuids, baselineStart, baselineEnd, powerMetrics);
                return new LoadCurveBuildResult(
                        VppCapabilityEvalHelper.buildLoadCurveFromTsdb(actualData, baselineData, statDate, powerMetrics),
                        "tsdb");
            }
            return new LoadCurveBuildResult(Collections.emptyList(), "tsdb");
        }
        // return new LoadCurveBuildResult(
        //         VppCapabilityEvalHelper.buildLoadCurve(resources, devices, siteId, statDate),
        //         "mock");
        return new LoadCurveBuildResult(Collections.emptyList(), null);
    }

    @Override
    public CommonPage<CapabilityHistoryVO> pageHistory(Long siteId, Map<String, Object> params) {
        resolveSite(siteId);
        Set<Long> resourceIds = listResources(siteId).stream()
                .map(VppResourcePoint::getId)
                .collect(Collectors.toSet());
        if (resourceIds.isEmpty()) {
            return emptyPage(params);
        }

        List<VppDrParticipation> participations = participationMapper.selectList(
                new LambdaQueryWrapper<VppDrParticipation>()
                        .in(VppDrParticipation::getResourceId, resourceIds)
                        .eq(VppDrParticipation::getParticipateStatus, PARTICIPATE_STATUS_ACCEPT)
                        .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED));
        Set<Long> eventIds = participations.stream()
                .map(VppDrParticipation::getEventId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (eventIds.isEmpty()) {
            return emptyPage(params);
        }

        List<VppDrEvent> events = eventMapper.selectBatchIds(eventIds).stream()
                .filter(e -> !VppAuditHelper.isDeleted(e.getDeleteFlag()))
                .sorted(Comparator.comparing(VppDrEvent::getStartTime, Comparator.nullsLast(Comparator.reverseOrder())))
                .collect(Collectors.toList());

        Map<Long, VppDrEvaluation> evaluationMap = evaluationMapper.selectList(
                new LambdaQueryWrapper<VppDrEvaluation>()
                        .in(VppDrEvaluation::getEventId, eventIds)
                        .eq(VppDrEvaluation::getDeleteFlag, VppAuditHelper.NOT_DELETED))
                .stream()
                .collect(Collectors.toMap(VppDrEvaluation::getEventId, e -> e, (a, b) -> a));

        List<CapabilityHistoryVO> records = events.stream()
                .map(event -> toHistoryVo(event, evaluationMap.get(event.getId())))
                .collect(Collectors.toList());

        long current = VppPageHelper.of(params).getCurrent();
        long size = VppPageHelper.of(params).getSize();
        int from = (int) Math.min((current - 1) * size, records.size());
        int to = (int) Math.min(from + size, records.size());
        List<CapabilityHistoryVO> pageRecords = from >= records.size()
                ? Collections.emptyList() : records.subList(from, to);
        return new CommonPage<>(pageRecords, (long) records.size(), current, size);
    }

    private CapabilityCategoryStatVO buildCategoryStat(String category,
                                                       List<VppResourcePoint> resources,
                                                       Map<Long, VppSite> siteMap) {
        CapabilityCategoryStatVO stat = new CapabilityCategoryStatVO();
        stat.setCategory(category);
        stat.setCategoryLabel(VppCapabilityEvalHelper.categoryLabel(category));
        stat.setCustomerCount(0);
        stat.setCapacityKw(BigDecimal.ZERO);

        Set<Long> customerIds = new HashSet<>();
        for (VppResourcePoint resource : resources) {
            if (!matchesCategory(category, resource.getResourceType())) {
                continue;
            }
            BigDecimal cap = resource.getCapacityKw() != null ? resource.getCapacityKw() : BigDecimal.ZERO;
            stat.setCapacityKw(stat.getCapacityKw().add(cap));
            Long customerId = siteResourceHelper.resolveCustomerId(resource, siteMap);
            if (customerId != null) {
                customerIds.add(customerId);
            }
        }
        stat.setCustomerCount(customerIds.size());
        return stat;
    }

    private boolean matchesCategory(String category, String resourceType) {
        if (VppCapabilityEvalHelper.CATEGORY_TOTAL.equals(category)) {
            return true;
        }
        return category.equals(VppCapabilityEvalHelper.resolveCategory(resourceType));
    }

    private CapabilityHistoryVO toHistoryVo(VppDrEvent event, VppDrEvaluation evaluation) {
        CapabilityHistoryVO vo = new CapabilityHistoryVO();
        vo.setEventId(event.getId());
        vo.setEventCode(event.getEventId());
        vo.setEventName(event.getEventName());
        vo.setEventType(event.getEventType());
        vo.setEventTypeLabel(VppCapabilityEvalHelper.eventTypeLabel(event.getEventType()));
        vo.setResponseType(event.getResponseType());
        vo.setResponseTypeLabel(VppCapabilityEvalHelper.responseTypeLabel(event.getResponseType()));
        vo.setStartTime(event.getStartTime());
        vo.setEndTime(event.getEndTime());
        vo.setTargetCapacityKw(event.getClearedCapacityKw() != null
                ? event.getClearedCapacityKw() : event.getTargetCapacityKw());
        if (evaluation != null) {
            vo.setActualCapacityKw(evaluation.getActualCapacityKw());
            vo.setQualifiedRate(evaluation.getQualifiedRate());
        }
        return vo;
    }

    private List<VppResourcePoint> listResources(Long siteId) {
        LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<VppResourcePoint>()
                .eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED);
        if (siteId != null) {
            wrapper.eq(VppResourcePoint::getSiteId, siteId);
        }
        return resourcePointMapper.selectList(wrapper);
    }

    private List<VppDevice> loadDevices(List<VppResourcePoint> resources) {
        Set<Long> deviceIds = resources.stream()
                .map(VppResourcePoint::getDeviceId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (deviceIds.isEmpty()) {
            return Collections.emptyList();
        }
        return deviceMapper.selectBatchIds(deviceIds).stream()
                .filter(d -> !VppAuditHelper.isDeleted(d.getDeleteFlag()))
                .collect(Collectors.toList());
    }

    private List<String> resolveDeviceUuids(List<VppResourcePoint> resources, List<VppDevice> devices) {
        if (resources == null || resources.isEmpty() || devices == null || devices.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, VppDevice> deviceMap = devices.stream()
                .collect(Collectors.toMap(VppDevice::getId, d -> d, (a, b) -> a));
        Set<String> uuids = new LinkedHashSet<>();
        for (VppResourcePoint resource : resources) {
            if (resource.getDeviceId() == null) {
                continue;
            }
            VppDevice device = deviceMap.get(resource.getDeviceId());
            if (device == null || !StringUtils.hasText(device.getDeviceUuid())) {
                continue;
            }
            uuids.add(device.getDeviceUuid().trim());
        }
        return new ArrayList<>(uuids);
    }

    private VppSite resolveSite(Long siteId) {
        if (siteId == null) {
            return null;
        }
        VppSite site = siteMapper.selectById(siteId);
        if (site == null || VppAuditHelper.isDeleted(site.getDeleteFlag())) {
            throw new BusinessException("站点不存在");
        }
        return site;
    }

    private LocalDate parseDate(String date) {
        if (!StringUtils.hasText(date)) {
            return LocalDate.now();
        }
        try {
            return LocalDate.parse(date.trim(), DATE_FMT);
        } catch (DateTimeParseException ex) {
            throw new BusinessException("日期格式无效，请使用 yyyy-MM-dd");
        }
    }

    private CommonPage<CapabilityHistoryVO> emptyPage(Map<String, Object> params) {
        long current = VppPageHelper.of(params).getCurrent();
        long size = VppPageHelper.of(params).getSize();
        return new CommonPage<>(new ArrayList<>(), 0L, current, size);
    }
}
