package com.usky.vpp.util;

import com.usky.vpp.config.VppUnProperties;
import com.usky.vpp.domain.VppResourcePoint;
import com.usky.vpp.domain.VppSite;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 构造运管平台标准响应报文（联调 JSON 实例）
 */
public final class VppUnMessageBuilder {

    private static final DateTimeFormatter CREATED_DATE_TIME =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final DateTimeFormatter MOMENT_DATE_TIME = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

    private VppUnMessageBuilder() {
    }

    public static Map<String, Object> buildTokenRequest(VppUnProperties properties) {
        return buildBaseRequest("TokenRequest", properties);
    }

    public static Map<String, Object> buildPollRequest(VppUnProperties properties) {
        return buildBaseRequest("Poll", properties);
    }

    public static Map<String, Object> buildCreateRegistrationRequest(VppUnProperties properties) {
        Map<String, Object> req = buildBaseRequest("CreateRegistrationRequest", properties);
        req.put("dnName", properties.getDnName());
        if (StringUtils.hasText(properties.getRegistrationId())) {
            req.put("registrationID", properties.getRegistrationId());
        }
        req.put("reportOnly", false);
        req.put("pullMode", true);
        req.put("signature", false);
        req.put("transport", "REST");
        req.put("transportAddress", properties.getTransportAddress());
        return req;
    }

    public static Map<String, Object> buildCreateOptRequest(String eventId, String optType,
                                                            List<Map<String, Object>> list,
                                                            VppUnProperties properties) {
        Map<String, Object> req = buildBaseRequest("CreateOptRequest", properties);
        req.put("eventID", eventId);
        req.put("optType", optType);
        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
        req.put("priceDownCoeff", properties.getPriceDownCoeff());
        if (list != null) {
            req.put("list", list);
        }
        return req;
    }

    public static Map<String, Object> buildCreateCqRequest(String eventId, String optType,
                                                           List<Map<String, Object>> list,
                                                           VppUnProperties properties) {
        Map<String, Object> req = buildBaseRequest("CreateCqRequest", properties);
        req.put("eventID", eventId);
        req.put("optType", optType != null ? optType : "optIn");
        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
        req.put("priceDownCoeff", properties.getPriceDownCoeff());
        if (list != null) {
            req.put("list", list);
        }
        return req;
    }

    public static Map<String, Object> buildCreateReportResponse(Map<String, Object> request, VppUnProperties properties) {
        Map<String, Object> resp = new LinkedHashMap<>();
        if (request != null) {
            resp.put("requestID", request.get("requestID"));
        }
        resp.put("root", "CreateReportResponse");
        resp.put("version", 1);
        resp.put("code", 200);
        resp.put("description", "ok");
        if (resp.get("requestID") == null) {
            resp.put("requestID", UUID.randomUUID().toString());
        }
        resp.put("dnID", properties.getDnId());
        Map<String, Object> pendingReports = new LinkedHashMap<>();
        List<String> reportRequestIds = new ArrayList<>();
        reportRequestIds.add("MetaDataReport");
        pendingReports.put("reportRequestID", reportRequestIds);
        resp.put("pendingReports", pendingReports);
        return resp;
    }

    /**
     * 构造 RegisterReportRequest：虚拟电厂量测点（含负荷预测）+ 站点/资源点有功功率（kW）。
     */
    public static Map<String, Object> buildRegisterReportRequest(VppUnProperties properties,
                                                                 List<VppSite> sites,
                                                                 List<VppResourcePoint> resources,
                                                                 Map<Long, String> deviceUnResourceIdMap) {
        Map<String, Object> req = buildBaseRequest("RegisterReportRequest", properties);
        req.put("reportRequestID", "MetaDataReport");
        List<Map<String, Object>> reports = new ArrayList<>();
        String createdDateTime = CREATED_DATE_TIME.format(LocalDateTime.now());
        for (VppUnRegisterReportIndex.Entry entry : VppUnRegisterReportIndex.buildRegisterEntries(
                properties, sites, resources, deviceUnResourceIdMap)) {
            String resourceId = resolveRegisterResourceId(entry, properties, deviceUnResourceIdMap);
            reports.add(buildVppMetadataReportItem(entry.getRid(), entry.getMetric(), resourceId, createdDateTime));
        }
        req.put("report", reports);
        return req;
    }

    private static String resolveRegisterResourceId(VppUnRegisterReportIndex.Entry entry,
                                                      VppUnProperties properties,
                                                      Map<Long, String> deviceUnResourceIdMap) {
        if (entry.getScope() == VppUnRegisterReportIndex.Scope.SITE) {
            return entry.getSite().getUnResourceId();
        }
        if (entry.getScope() == VppUnRegisterReportIndex.Scope.RESOURCE) {
            return deviceUnResourceIdMap.get(entry.getResource().getDeviceId());
        }
        return properties.getDnId();
    }

    public static Map<String, Object> buildIntervalDataReportRequest(VppUnProperties properties,
                                                                     Map<String, Object> createReportRequest,
                                                                     List<Map<String, Object>> pointCurveData,
                                                                     LocalDateTime reportTime) {
        Map<String, Object> req = buildBaseRequest("IntervalDataReportRequest", properties);
        req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "IntervalDataReport"));
        LocalDateTime createdAt = reportTime != null ? reportTime : LocalDateTime.now();
        req.put("createdDateTime", MOMENT_DATE_TIME.format(createdAt));
        req.put("pointCurveData", pointCurveData != null ? pointCurveData : new ArrayList<>());
        return req;
    }

    public static Map<String, Object> buildIntervalCurveDataEntry(int rid, List<Map<String, Object>> values) {
        Map<String, Object> entry = new LinkedHashMap<>();
        entry.put("rID", rid);
        Map<String, Object> irregular = new LinkedHashMap<>();
        irregular.put("values", values != null ? values : new ArrayList<>());
        entry.put("irregular", irregular);
        return entry;
    }

    public static Map<String, Object> buildIntervalCurveValue(BigDecimal value,
                                                              LocalDateTime timestamp,
                                                              String quality) {
        Map<String, Object> point = new LinkedHashMap<>();
        point.put("value", value != null ? value : BigDecimal.ZERO);
        point.put("timestamp", MOMENT_DATE_TIME.format(timestamp != null ? timestamp : LocalDateTime.now()));
        point.put("quality", StringUtils.hasText(quality) ? quality : "good");
        return point;
    }

    public static Map<String, Object> buildMomentDataReportRequest(VppUnProperties properties,
                                                                   Map<String, Object> createReportRequest,
                                                                   List<Map<String, Object>> pointData,
                                                                   LocalDateTime reportTime) {
        Map<String, Object> req = buildBaseRequest("MomentDataReportRequest", properties);
        req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "MomentDataReport"));
        LocalDateTime createdAt = reportTime != null ? reportTime : LocalDateTime.now();
        req.put("createdDateTime", MOMENT_DATE_TIME.format(createdAt));
        req.put("pointData", pointData != null ? pointData : new ArrayList<>());
        return req;
    }

    public static Map<String, Object> buildMomentDataPoint(int rid,
                                                           BigDecimal value,
                                                           LocalDateTime timestamp,
                                                           String quality) {
        Map<String, Object> point = new LinkedHashMap<>();
        point.put("rID", rid);
        point.put("value", value != null ? value : BigDecimal.ZERO);
        point.put("timestamp", MOMENT_DATE_TIME.format(timestamp != null ? timestamp : LocalDateTime.now()));
        point.put("quality", StringUtils.hasText(quality) ? quality : "good");
        return point;
    }

    public static Map<String, Object> buildLoadForecastReportRequest(Map<String, Object> createReportRequest,
                                                                   List<VppResourcePoint> resources,
                                                                   Map<Long, VppSite> siteMap,
                                                                   VppUnProperties properties) {
        Map<String, Object> req = buildBaseRequest("LoadForecastReportRequest", properties);
        req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "LoadForecastReport"));
        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
        req.put("pointData", buildPointData(resources, siteMap, properties));
        return req;
    }

    public static Map<String, Object> buildRegulateForecastReportRequest(Map<String, Object> createReportRequest,
                                                                   List<VppResourcePoint> resources,
                                                                   Map<Long, VppSite> siteMap,
                                                                   VppUnProperties properties) {
        Map<String, Object> req = buildBaseRequest("RegulateForecastReportRequest", properties);
        req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "RegulateForecastReport"));
        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
        req.put("pointData", buildPointData(resources, siteMap, properties));
        return req;
    }

    private static Map<String, Object> buildVppMetadataReportItem(int rid,
                                                                  VppUnRegisterMetricDefinitions.RegisterMetric metric,
                                                                  String resourceId,
                                                                  String createdDateTime) {
        Map<String, Object> report = new LinkedHashMap<>();
        report.put("createdDateTime", createdDateTime);
        Map<String, Object> description = new LinkedHashMap<>();
        description.put("rID", rid);
        Map<String, Object> metricBody = new LinkedHashMap<>();
        metricBody.put("metricName", metric.getMetricName());
        if (StringUtils.hasText(metric.getMultiplier())) {
            metricBody.put("multiplier", metric.getMultiplier());
        }
        metricBody.put("symbol", metric.getSymbol());
        description.put("metric", metricBody);
        Map<String, Object> dataSource = new LinkedHashMap<>();
        dataSource.put("resourceID", java.util.Collections.singletonList(resourceId));
        description.put("reportDataSource", dataSource);
        report.put("reportDescription", description);
        return report;
    }

    private static List<Map<String, Object>> buildPointData(List<VppResourcePoint> resources,
                                                            Map<Long, VppSite> siteMap,
                                                            VppUnProperties properties) {
        List<Map<String, Object>> pointData = new ArrayList<>();
        if (resources == null || resources.isEmpty()) {
            Map<String, Object> point = new LinkedHashMap<>();
            point.put("resourceID", properties.getDnId());
            point.put("value", "0");
            point.put("timestamp", CREATED_DATE_TIME.format(LocalDateTime.now()));
            pointData.add(point);
            return pointData;
        }
        String timestamp = CREATED_DATE_TIME.format(LocalDateTime.now());
        for (VppResourcePoint resource : resources) {
            Map<String, Object> point = new LinkedHashMap<>();
            VppSite site = siteMap != null ? siteMap.get(resource.getSiteId()) : null;
            point.put("resourceID", resolveUnResourceId(resource, site, properties));
            BigDecimal value = resource.getMaxUpKw() != null ? resource.getMaxUpKw() : BigDecimal.ZERO;
            point.put("value", value.stripTrailingZeros().toPlainString());
            point.put("timestamp", timestamp);
            pointData.add(point);
        }
        return pointData;
    }

    private static String resolveUnResourceId(VppResourcePoint resource, VppSite site, VppUnProperties properties) {
        if (site != null && StringUtils.hasText(site.getUnResourceId())) {
            return site.getUnResourceId();
        }
        if (StringUtils.hasText(resource.getResourceCode())) {
            return resource.getResourceCode();
        }
        return properties.getDnId();
    }

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

    public static Map<String, Object> buildOptListItem(String account, BigDecimal loadKw, String needZk) {
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("account", account);
        item.put("load", loadKw != null ? loadKw.stripTrailingZeros().toPlainString() : "0");
        if (StringUtils.hasText(needZk)) {
            item.put("needzk", needZk);
        }
        return item;
    }

    private static Map<String, Object> buildBaseRequest(String root, VppUnProperties properties) {
        Map<String, Object> req = new LinkedHashMap<>();
        req.put("root", root);
        req.put("version", 1);
        req.put("requestID", UUID.randomUUID().toString());
        req.put("dnID", properties.getDnId());
        return req;
    }

    public static Map<String, Object> buildCreateOptResponse(Map<String, Object> request, VppUnProperties properties) {
        Map<String, Object> resp = copyRequestAsResponse(request, "CreateOptResponse");
        fillCommon(resp, properties);
        resp.put("code", 200);
        resp.put("description", "ok");
        return resp;
    }

    public static Map<String, Object> buildCreateCqResponse(Map<String, Object> request, VppUnProperties properties) {
        Map<String, Object> resp = copyRequestAsResponse(request, "CreateCqResponse");
        fillCommon(resp, properties);
        resp.put("code", 200);
        resp.put("description", "ok");
        return resp;
    }

    public static Map<String, Object> buildCreateEventResponse(String eventId, String requestId,
                                                               String optType, VppUnProperties properties) {
        Map<String, Object> resp = new LinkedHashMap<>();
        resp.put("root", "CreateEventResponse");
        resp.put("version", 1);
        resp.put("code", 200);
        resp.put("description", "ok");
        resp.put("requestID", requestId != null ? requestId : UUID.randomUUID().toString());
        resp.put("dnID", properties.getDnId());

        Map<String, Object> eventResponse = new LinkedHashMap<>();
        eventResponse.put("optType", optType != null ? optType : "optIn");
        eventResponse.put("code", 200);
        eventResponse.put("description", "ok");
        eventResponse.put("requestID", UUID.randomUUID().toString());

        Map<String, Object> qualifiedEventId = new HashMap<>();
        qualifiedEventId.put("eventID", eventId);
        qualifiedEventId.put("modificationNumber", 0);
        eventResponse.put("qualifiedEventID", qualifiedEventId);

        List<Map<String, Object>> eventResponses = new ArrayList<>();
        eventResponses.add(eventResponse);
        resp.put("eventResponses", eventResponses);
        return resp;
    }

    private static Map<String, Object> copyRequestAsResponse(Map<String, Object> request, String root) {
        Map<String, Object> resp = new LinkedHashMap<>();
        if (request != null) {
            resp.putAll(request);
        }
        resp.put("root", root);
        resp.put("version", request != null && request.get("version") != null ? request.get("version") : 1);
        return resp;
    }

    private static void fillCommon(Map<String, Object> resp, VppUnProperties properties) {
        if (resp.get("requestID") == null) {
            resp.put("requestID", UUID.randomUUID().toString());
        }
        resp.put("dnID", properties.getDnId());
    }
}
