package com.cmvr.quic.gateway.config;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import org.springframework.core.env.Environment;

/**
 * QUIC Gateway运行配置。
 *
 * <p>由Spring Environment读取主应用的{@code cmvr.quic.*}配置，同时兼容原有的
 * JVM参数和环境变量。</p>
 */
public final class GatewayConfig {

    private final String bindHost;
    private final int quicPort;
    private final String alpn;
    private final Path certificateFile;
    private final Path privateKeyFile;
    private final Path clientCaFile;
    private final boolean requireClientCertificate;
    private final Set<String> allowedNodeIds;
    private final int heartbeatIntervalMs;
    private final int heartbeatTimeoutMs;
    private final int idleTimeoutMs;
    private final int ioThreads;
    private final int maximumControlFrameBytes;
    private final int datagramQueueLength;
    private final int maximumFrameBytes;
    private final long maximumReassemblyBytes;
    private final int maximumReassemblyFrames;
    private final int reassemblyTimeoutMs;
    private final int mediaSubscriberQueueCapacity;

    private GatewayConfig(Properties properties) {
        bindHost = value(properties, "cmvr.quic.bind-host", "CMVR_QUIC_BIND_HOST", "0.0.0.0");
        quicPort = integer(properties, "cmvr.quic.port", "CMVR_QUIC_PORT", 4433);
        alpn = value(properties, "cmvr.quic.alpn", "CMVR_QUIC_ALPN", "cmvr-quic-edge/1");
        certificateFile = path(properties, "cmvr.quic.tls.certificate", "CMVR_QUIC_TLS_CERTIFICATE");
        privateKeyFile = path(properties, "cmvr.quic.tls.private-key", "CMVR_QUIC_TLS_PRIVATE_KEY");
        clientCaFile = optionalPath(properties, "cmvr.quic.tls.client-ca", "CMVR_QUIC_TLS_CLIENT_CA");
        requireClientCertificate = bool(properties, "cmvr.quic.tls.require-client-certificate",
                "CMVR_QUIC_TLS_REQUIRE_CLIENT_CERTIFICATE", false);
        allowedNodeIds = stringSet(properties, "cmvr.quic.allowed-node-ids",
                "CMVR_QUIC_ALLOWED_NODE_IDS");
        heartbeatIntervalMs = integer(properties, "cmvr.quic.heartbeat-interval-ms",
                "CMVR_QUIC_HEARTBEAT_INTERVAL_MS", 5000);
        heartbeatTimeoutMs = integer(properties, "cmvr.quic.heartbeat-timeout-ms",
                "CMVR_QUIC_HEARTBEAT_TIMEOUT_MS", 15000);
        idleTimeoutMs = integer(properties, "cmvr.quic.idle-timeout-ms", "CMVR_QUIC_IDLE_TIMEOUT_MS", 30000);
        ioThreads = integer(properties, "cmvr.quic.io-threads", "CMVR_QUIC_IO_THREADS",
                Math.max(2, Runtime.getRuntime().availableProcessors()));
        maximumControlFrameBytes = integer(properties, "cmvr.quic.maximum-control-frame-bytes",
                "CMVR_QUIC_MAXIMUM_CONTROL_FRAME_BYTES", 1024 * 1024);
        datagramQueueLength = integer(properties, "cmvr.quic.datagram-queue-length",
                "CMVR_QUIC_DATAGRAM_QUEUE_LENGTH", 1024);
        maximumFrameBytes = integer(properties, "cmvr.quic.maximum-frame-bytes",
                "CMVR_QUIC_MAXIMUM_FRAME_BYTES", 8 * 1024 * 1024);
        maximumReassemblyBytes = longValue(properties, "cmvr.quic.maximum-reassembly-bytes",
                "CMVR_QUIC_MAXIMUM_REASSEMBLY_BYTES", 64L * 1024L * 1024L);
        maximumReassemblyFrames = integer(properties, "cmvr.quic.maximum-reassembly-frames",
                "CMVR_QUIC_MAXIMUM_REASSEMBLY_FRAMES", 256);
        reassemblyTimeoutMs = integer(properties, "cmvr.quic.reassembly-timeout-ms",
                "CMVR_QUIC_REASSEMBLY_TIMEOUT_MS", 2000);
        mediaSubscriberQueueCapacity = integer(properties, "cmvr.quic.media-subscriber-queue-capacity",
                "CMVR_QUIC_MEDIA_SUBSCRIBER_QUEUE_CAPACITY", 64);
        validate();
    }

    public static GatewayConfig load(Environment environment) {
        Properties properties = new Properties();
        String[] keys = {
                "cmvr.quic.bind-host", "cmvr.quic.port", "cmvr.quic.alpn",
                "cmvr.quic.allowed-node-ids",
                "cmvr.quic.tls.certificate", "cmvr.quic.tls.private-key",
                "cmvr.quic.tls.client-ca", "cmvr.quic.tls.require-client-certificate",
                "cmvr.quic.heartbeat-interval-ms", "cmvr.quic.heartbeat-timeout-ms",
                "cmvr.quic.idle-timeout-ms", "cmvr.quic.io-threads",
                "cmvr.quic.maximum-control-frame-bytes", "cmvr.quic.datagram-queue-length",
                "cmvr.quic.maximum-frame-bytes", "cmvr.quic.maximum-reassembly-bytes",
                "cmvr.quic.maximum-reassembly-frames", "cmvr.quic.reassembly-timeout-ms",
                "cmvr.quic.media-subscriber-queue-capacity"
        };
        for (String key : keys) {
            String configured = environment.getProperty(key);
            if (hasText(configured)) {
                properties.setProperty(key, configured);
            }
        }
        return new GatewayConfig(properties);
    }

    private void validate() {
        requirePort(quicPort, "QUIC监听端口");
        if (alpn.isEmpty() || alpn.length() > 255) {
            throw new IllegalArgumentException("ALPN长度必须在1到255之间");
        }
        requireFile(certificateFile, "TLS证书");
        requireFile(privateKeyFile, "TLS私钥");
        if (requireClientCertificate) {
            requireFile(clientCaFile, "客户端CA证书");
        }
        if (heartbeatIntervalMs < 250 || heartbeatIntervalMs > 3600000) {
            throw new IllegalArgumentException("心跳间隔必须在250到3600000毫秒之间");
        }
        if (heartbeatTimeoutMs <= heartbeatIntervalMs) {
            throw new IllegalArgumentException("业务心跳超时必须大于心跳间隔");
        }
        if (idleTimeoutMs <= heartbeatIntervalMs) {
            throw new IllegalArgumentException("QUIC空闲超时必须大于心跳间隔");
        }
        if (ioThreads <= 0 || maximumControlFrameBytes <= 0 || datagramQueueLength <= 0
                || maximumFrameBytes <= 0 || maximumReassemblyBytes < maximumFrameBytes
                || maximumReassemblyFrames <= 0 || reassemblyTimeoutMs <= 0
                || mediaSubscriberQueueCapacity <= 0) {
            throw new IllegalArgumentException("Gateway容量或超时配置不合法");
        }
    }

    private static void requirePort(int port, String name) {
        if (port <= 0 || port > 65535) {
            throw new IllegalArgumentException(name + "必须在1到65535之间");
        }
    }

    private static void requireFile(Path path, String name) {
        if (path == null || !Files.isRegularFile(path)) {
            throw new IllegalArgumentException(name + "不存在或不是文件: " + path);
        }
    }

    private static String value(Properties properties, String key, String env, String defaultValue) {
        String systemValue = System.getProperty(key);
        if (hasText(systemValue)) {
            return systemValue.trim();
        }
        String environmentValue = System.getenv(env);
        if (hasText(environmentValue)) {
            return environmentValue.trim();
        }
        String fileValue = properties.getProperty(key);
        return hasText(fileValue) ? fileValue.trim() : defaultValue;
    }

    private static int integer(Properties properties, String key, String env, int defaultValue) {
        return Integer.parseInt(value(properties, key, env, String.valueOf(defaultValue)));
    }

    private static long longValue(Properties properties, String key, String env, long defaultValue) {
        return Long.parseLong(value(properties, key, env, String.valueOf(defaultValue)));
    }

    private static boolean bool(Properties properties, String key, String env, boolean defaultValue) {
        return Boolean.parseBoolean(value(properties, key, env, String.valueOf(defaultValue)));
    }

    private static Set<String> stringSet(Properties properties, String key, String env) {
        String raw = value(properties, key, env, "");
        if (!hasText(raw)) {
            return Collections.emptySet();
        }
        Set<String> values = new LinkedHashSet<String>();
        for (String item : raw.split(",")) {
            if (hasText(item)) {
                values.add(item.trim());
            }
        }
        return Collections.unmodifiableSet(values);
    }

    private static Path path(Properties properties, String key, String env) {
        String raw = value(properties, key, env, "");
        return raw.isEmpty() ? null : Paths.get(raw).toAbsolutePath().normalize();
    }

    private static Path optionalPath(Properties properties, String key, String env) {
        return path(properties, key, env);
    }

    private static boolean hasText(String value) {
        return value != null && !value.trim().isEmpty();
    }

    public String getBindHost() { return bindHost; }
    public int getQuicPort() { return quicPort; }
    public String getAlpn() { return alpn; }
    public Path getCertificateFile() { return certificateFile; }
    public Path getPrivateKeyFile() { return privateKeyFile; }
    public Path getClientCaFile() { return clientCaFile; }
    public boolean isRequireClientCertificate() { return requireClientCertificate; }
    public Set<String> getAllowedNodeIds() { return allowedNodeIds; }
    public boolean isNodeAllowed(String nodeId) {
        return allowedNodeIds.isEmpty() || allowedNodeIds.contains(nodeId);
    }
    public int getHeartbeatIntervalMs() { return heartbeatIntervalMs; }
    public int getHeartbeatTimeoutMs() { return heartbeatTimeoutMs; }
    public int getIdleTimeoutMs() { return idleTimeoutMs; }
    public int getIoThreads() { return ioThreads; }
    public int getMaximumControlFrameBytes() { return maximumControlFrameBytes; }
    public int getDatagramQueueLength() { return datagramQueueLength; }
    public int getMaximumFrameBytes() { return maximumFrameBytes; }
    public long getMaximumReassemblyBytes() { return maximumReassemblyBytes; }
    public int getMaximumReassemblyFrames() { return maximumReassemblyFrames; }
    public int getReassemblyTimeoutMs() { return reassemblyTimeoutMs; }
    public int getMediaSubscriberQueueCapacity() { return mediaSubscriberQueueCapacity; }
}
