class ClashConfig {
  final String logLevel;
  final bool? ipv6;
  final bool? allowLan;
  final String? bindAddress;
  final String mode;
  final int mixedPort;
  final String? externalController;
  final bool? unifiedDelay;
  final bool? geodataMode;
  final bool? tcpConcurrent;
  final String? findProcessMode;
  final String? globalClientFingerprint;
  final Map<String, bool>? profile;
  final Sniffer? sniffer;
  final DNS? dns;
  final Tun? tun;
  final List<Map<String, dynamic>>? proxies;
  final List<ProxyGroup>? proxyGroups;
  final List<String>? rules;

  ClashConfig({
    required this.logLevel,
    this.ipv6,
    this.allowLan,
    this.bindAddress,
    required this.mode,
    required this.mixedPort,
    this.externalController,
    this.unifiedDelay,
    this.geodataMode,
    this.tcpConcurrent,
    this.findProcessMode,
    this.globalClientFingerprint,
    this.profile,
    this.sniffer,
    this.dns,
    this.tun,
    this.proxies,
    this.proxyGroups,
    this.rules,
  });

  factory ClashConfig.fromJson(Map<String, dynamic> json) {
    return ClashConfig(
      logLevel: json['log-level'],
      ipv6: json['ipv6'],
      allowLan: json['allow-lan'],
      bindAddress: json['bind-address'],
      mode: json['mode'],
      mixedPort: json['mixed-port'],
      externalController: json['external-controller'],
      unifiedDelay: json['unified-delay'],
      geodataMode: json['geodata-mode'],
      tcpConcurrent: json['tcp-concurrent'],
      findProcessMode: json['find-process-mode'],
      globalClientFingerprint: json['global-client-fingerprint'],
      profile: json['profile'] != null ? Map<String, bool>.from(json['profile']) : null,
      sniffer: json['sniffer'] != null ? Sniffer.fromJson(json['sniffer']) : null,
      dns: DNS.fromJson(json['dns']),
      tun: json['tun'] != null ? Tun.fromJson(json['tun']) : null,
      proxies: List<Map<String, dynamic>>.from(json['proxies']),
      proxyGroups: (json['proxy-groups'] as List)
          .map((e) => ProxyGroup.fromJson(e))
          .toList(),
      rules: List<String>.from(json['rules']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'log-level': logLevel,
      if (ipv6 != null) 'ipv6': ipv6,
      if (allowLan != null) 'allow-lan': allowLan,
      if (bindAddress != null) 'bind-address': bindAddress,
      'mode': mode,
      'mixed-port': mixedPort,
      if (externalController != null) 'external-controller': externalController,
      if (unifiedDelay != null) 'unified-delay': unifiedDelay,
      if (geodataMode != null) 'geodata-mode': geodataMode,
      if (tcpConcurrent != null) 'tcp-concurrent': tcpConcurrent,
      if (findProcessMode != null) 'find-process-mode': findProcessMode,
      if (globalClientFingerprint != null) 'global-client-fingerprint': globalClientFingerprint,
      if (profile != null) 'profile': profile,
      if (sniffer != null) 'sniffer': sniffer!.toJson(),
      if (dns != null)  'dns': dns?.toJson(),
      if (tun != null) 'tun': tun!.toJson(),
      if (proxies != null) 'proxies': proxies,
      if (proxyGroups != null)'proxy-groups': proxyGroups?.map((e) => e.toJson()).toList(),
      'rules': rules,
    };
  }
}

class BaseProxy {
  final String name;
  final String server;
  final int port;
  final bool? udp;

  BaseProxy({
    required this.name,
    required this.server,
    required this.port,
    this.udp,
  });

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'server': server,
      'port': port,
      if (udp != null) 'udp': udp,
    };
  }
}

class SSProxy extends BaseProxy {
  final String type;
  final String password;
  final String cipher;
  final String? plugin;
  final Map<String, dynamic>? pluginOpts;

  SSProxy({
    required String name,
    required String server,
    required int port,
    bool? udp,
    required this.type,
    required this.password,
    required this.cipher,
    this.plugin,
    this.pluginOpts,
  }) : super(name: name, server: server, port: port, udp: udp);

  factory SSProxy.fromJson(Map<String, dynamic> json) {
    return SSProxy(
      name: json['name'],
      server: json['server'],
      port: json['port'],
      udp: json['udp'],
      type: json['type'],
      password: json['password'],
      cipher: json['cipher'],
      plugin: json['plugin'],
      pluginOpts: json['plugin-opts'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'type': type,
      'server': server,
      'port': port,
      'password': password,
      'cipher': cipher,
      if (udp != null) 'udp': udp,
      if (plugin != null) 'plugin': plugin,
      if (pluginOpts != null) 'plugin-opts': pluginOpts,
    };
  }
}

class VlessProxy extends BaseProxy {
  final String uuid;
  final String? flow;
  final bool tls;
  final String? servername;
  final String network;
  final Map<String, dynamic>? smux;
  final List<String>? alpn;
  final String? fingerprint;
  final String? clientFingerprint;
  final bool? skipCertVerify;
  final Map<String, dynamic>? realityOpts;

  VlessProxy({
    required String name,
    required String server,
    required int port,
    bool? udp,
    required this.uuid,
    this.flow,
    required this.tls,
    this.servername,
    required this.network,
    this.smux,
    this.alpn,
    this.fingerprint,
    this.clientFingerprint,
    this.skipCertVerify,
    this.realityOpts,
  }) : super(name: name, server: server, port: port, udp: udp);

  factory VlessProxy.fromJson(Map<String, dynamic> json) {
    return VlessProxy(
      name: json['name'],
      server: json['server'],
      port: json['port'],
      udp: json['udp'] ?? false,
      uuid: json['uuid'],
      flow: json['flow'],
      tls: json['tls'],
      servername: json['servername'],
      network: json['network'],
      smux: json['smux'],
      alpn: json['alpn']?.cast<String>(),
      fingerprint: json['fingerprint'],
      clientFingerprint: json['client-fingerprint'],
      skipCertVerify: json['skip-cert-verify'],
      realityOpts: json['reality-opts'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'type': 'vless',
      'server': server,
      'port': port,
      'udp': udp,
      'uuid': uuid,
      if (flow != null) 'flow': flow,
      'tls': tls,
      if (servername != null) 'servername': servername,
      'network': network,
      if (smux != null) 'smux': smux,
      if (alpn != null) 'alpn': alpn,
      if (fingerprint != null) 'fingerprint': fingerprint,
      if (clientFingerprint != null) 'client-fingerprint': clientFingerprint,
      if (skipCertVerify != null) 'skip-cert-verify': skipCertVerify,
      if (realityOpts != null) 'reality-opts': realityOpts,
    };
  }
}

class VmessProxy extends BaseProxy {
  final String uuid;
  final int? alterId;
  final String? cipher;
  final String? network;
  final Map<String, dynamic>? wsOpts;
  final String? servername;
  final bool? skipCertVerify;
  final List<String>? alpn;

  VmessProxy({
    required String name,
    required String server,
    required int port,
    bool? udp,
    required this.uuid,
    this.alterId,
    this.cipher,
    this.network = 'tcp',
    this.wsOpts,
    this.servername,
    this.skipCertVerify,
    this.alpn,
  }) : super(name: name, server: server, port: port, udp: udp);

  factory VmessProxy.fromJson(Map<String, dynamic> json) {
    return VmessProxy(
      name: json['name'],
      server: json['server'],
      port: json['port'],
      udp: json['udp'],
      uuid: json['uuid'],
      alterId: json['alterId'],
      cipher: json['cipher'],
      network: json['network'] ?? 'tcp',
      wsOpts: json['ws-opts'],
      servername: json['servername'],
      skipCertVerify: json['skip-cert-verify'],
      alpn: json['alpn']?.cast<String>(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'type': 'vmess',
      'server': server,
      'port': port,
      'uuid': uuid,
      'udp': udp,
      if (alterId != null) 'alterId': alterId,
      if (cipher != null) 'cipher': cipher,
      'network': network,
      if (wsOpts != null) 'ws-opts': wsOpts,
      if (servername != null) 'servername': servername,
      if (skipCertVerify != null) 'skip-cert-verify': skipCertVerify,
      if (alpn != null) 'alpn': alpn,
    };
  }
}

class TrojanProxy extends BaseProxy {
  final String password;
  final String? sni;
  final String? network;
  final String? clientFingerprint;
  final String? fingerprint;
  final bool? skipCertVerify;

  TrojanProxy({
    required String name,
    required String server,
    required int port,
    bool? udp,
    required this.password,
    this.sni,
    this.network,
    this.clientFingerprint,
    this.fingerprint,
    this.skipCertVerify,
  }) : super(name: name, server: server, port: port, udp: udp);

  factory TrojanProxy.fromJson(Map<String, dynamic> json) {
    return TrojanProxy(
      name: json['name'],
      server: json['server'],
      port: json['port'],
      udp: json['udp'],
      password: json['password'],
      sni: json['sni'],
      network: json['network'],
      clientFingerprint: json['client-fingerprint'],
      fingerprint: json['fingerprint'],
      skipCertVerify: json['skip-cert-verify'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'type': 'trojan',
      'server': server,
      'port': port,
      'password': password,
      'udp': udp,
      if (sni != null) 'sni': sni,
      if (network != null) 'network': network,
      if (clientFingerprint != null) 'client-fingerprint': clientFingerprint,
      if (fingerprint != null) 'fingerprint': fingerprint,
      if (skipCertVerify != null) 'skip-cert-verify': skipCertVerify,
    };
  }
}

class ProxyGroup {
  final String name;
  final String type;
  final List<String> proxies;

  ProxyGroup({
    required this.name,
    required this.type,
    required this.proxies,
  });

  factory ProxyGroup.fromJson(Map<String, dynamic> json) {
    return ProxyGroup(
      name: json['name'],
      type: json['type'],
      proxies: List<String>.from(json['proxies']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'type': type,
      'proxies': proxies,
    };
  }
}

class DNS {
  final bool enable;
  final String listen;
  final bool ipv6;
  final String enhancedMode;
  final List<String>? fakeIpFilter;
  final List<String> nameserver;
  final List<String> proxyServerNameserver;
  final Map<String, List<String>> nameserverPolicy;

  DNS({
    required this.enable,
    required this.listen,
    required this.ipv6,
    required this.enhancedMode,
    this.fakeIpFilter,
    required this.nameserver,
    required this.proxyServerNameserver,
    required this.nameserverPolicy,
  });

  factory DNS.fromJson(Map<String, dynamic> json) {
    return DNS(
      enable: json['enable'],
      listen: json['listen'],
      ipv6: json['ipv6'],
      enhancedMode: json['enhanced-mode'],
      fakeIpFilter: json['fake-ip-filter'] != null ? List<String>.from(json['fake-ip-filter']) : null,
      nameserver: List<String>.from(json['nameserver']),
      proxyServerNameserver: List<String>.from(json['proxy-server-nameserver']),
      nameserverPolicy: Map<String, List<String>>.from(json['nameserver-policy'].map(
        (key, value) => MapEntry(key, List<String>.from(value)),
      )),
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = {
      'enable': enable,
      'listen': listen,
      'ipv6': ipv6,
      'enhanced-mode': enhancedMode,
      'nameserver': nameserver,
      'proxy-server-nameserver': proxyServerNameserver,
      'nameserver-policy': nameserverPolicy,
    };
    if (fakeIpFilter != null) {
      data['fake-ip-filter'] = fakeIpFilter;
    }
    return data;
  }
}

class Sniffer {
  final bool enable;
  final Map<String, Map<String, dynamic>> sniff;
  final List<String> skipDomain;

  Sniffer({
    required this.enable,
    required this.sniff,
    required this.skipDomain,
  });

  factory Sniffer.fromJson(Map<String, dynamic> json) {
    return Sniffer(
      enable: json['enable'],
      sniff: Map<String, Map<String, dynamic>>.from(json['sniff']),
      skipDomain: List<String>.from(json['skip-domain']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'enable': enable,
      'sniff': sniff,
      'skip-domain': skipDomain,
    };
  }
}

class Tun {
  final bool enable;
  final String stack;
  final bool autoRoute;
  final bool autoRedirect;
  final bool autoDetectInterface;
  final List<String> dnsHijack;

  Tun({
    required this.enable,
    required this.stack,
    required this.autoRoute,
    required this.autoRedirect,
    required this.autoDetectInterface,
    required this.dnsHijack,
  });

  factory Tun.fromJson(Map<String, dynamic> json) {
    return Tun(
      enable: json['enable'],
      stack: json['stack'],
      autoRoute: json['auto-route'],
      autoRedirect: json['auto-redirect'],
      autoDetectInterface: json['auto-detect-interface'],
      dnsHijack: List<String>.from(json['dns-hijack']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'enable': enable,
      'stack': stack,
      'auto-route': autoRoute,
      'auto-redirect': autoRedirect,
      'auto-detect-interface': autoDetectInterface,
      'dns-hijack': dnsHijack,
    };
  }
}

// 示例用法
final sniffer = Sniffer(
  enable: true,
  sniff: {
    'HTTP': {
      'ports': [80, '8080-8880'],
      'override-destination': true,
    },
    'TLS': {
      'ports': [443, 8443],
    },
    'QUIC': {
      'ports': [443, 8443],
    },
  },
  skipDomain: ['Mijia Cloud'],
);
