import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:get/get.dart';
import 'package:yaml_edit/yaml_edit.dart';
import 'package:path/path.dart' as path;
import '../../common/constants.dart';
import '../../const/const.dart';
import '../../controller/controllers.dart';
import '../../data/model/NodeMode.dart';
import '../../utils/shell.dart';
import '../../utils/utils.dart';
import '../mode/clash_config.dart';




class ClashService extends GetxController {
  Process? clashCoreProcess;
  final coreStatus = RunningState.stoped.obs;
  final serviceMode = false.obs;
  bool get clashServiceIsRuning => coreStatus.value == RunningState.running;


  Future<void> updatePorts() async {

    // 检查端口占用
    int newPort = await findAvailablePort(controllers.config.mixedPort.value+1);
    controllers.config.mixedPort.value = newPort;

    controllers.global.updateMsg("混合端口已更新为: $newPort");
    // 检查API端口占用
    int newApiPort = await findAvailablePort(controllers.config.apiAddressPort.value);
    controllers.config.apiAddressPort.value = newApiPort;
    controllers.global.updateMsg("API端口已更新为: $newApiPort");

    int newDnsPort = await findAvailablePort(int.parse(controllers.config.dnsPort.value.split(':').last));
    controllers.config.dnsPort.value = '${controllers.config.dnsPort.value.split(':').first}:$newDnsPort';

    await controllers.config.saveConfig();
    //await controllers.config.readClashCoreApi();

  }
  Future<void> makeInitConfig() async {
    //await updatePorts();
    //await controllers.config.readClashCoreApi();
    var mode = controllers.global.modesSelect.value;

    var clashConfig = ClashConfig(
        mixedPort: controllers.config.mixedPort.value,
        allowLan: true,
        bindAddress: '*',
        mode: mode,
        logLevel: 'debug',
        externalController: '127.0.0.1:${controllers.config.apiAddressPort.value}',
        unifiedDelay: false,
        geodataMode: true,
        tcpConcurrent: false,
        findProcessMode: 'strict',
        globalClientFingerprint: 'chrome',
        dns: DNS(
          enable: false,
          listen: controllers.config.dnsPort.value,
          ipv6: false,
          enhancedMode: kRedirHostMode,
          fakeIpFilter: null,
          nameserver: kDomesticDNS,
          proxyServerNameserver: kDomesticDNS,
          nameserverPolicy: {
            'geosite:cn,private': kDomesticDNS,
            'geosite:geolocation-!cn': kForeignDNS,
          },
        ),
        tun: Tun(
          enable: false,
          stack: 'system',
          autoRoute: true,
          autoRedirect: true,
          autoDetectInterface: true,
          dnsHijack: ['any:53'],
        ),
        proxies: [],

        rules: [
          'GEOIP,CN,DIRECT',
          'MATCH,DIRECT'
        ]
    );
    try {

      final file = File(path.join(Paths.config.path, Files.makeInitProxyConfig.path ));
      await file.writeAsString(clashConfig.toYaml());
      print('配置文件已成功保存到: ${file.path}');
    } catch (e) {
      print('保存配置文件时发生错误: $e');
      throw Exception('无法保存配置文件');
    }


  }

  Future<bool> startClashCore() async {
    final timeout = const Duration(seconds: 30);
    final checkInterval = const Duration(milliseconds: 200);
    var startTime = DateTime.now();

    await controllers.config.readClashCoreApi();

    try {
      controllers.global.updateMsg("启动内核---${controllers.config.config.value.selected}");
      if (controllers.config.config.value.selected == controllers.config.config.value.selected) {
        controllers.global.updateMsg("启动内核初始化");
      } else {
        controllers.global.updateMsg("启动内核");
      }
      coreStatus.value = RunningState.starting;

      int? exitCode;
      clashCoreProcess = await Process.start(
          Files.assetsCCore.path,
          ['-d', Paths.config.path, '-f', path.join(Paths.config.path, controllers.config.config.value.selected)],
          mode: ProcessStartMode.inheritStdio
      );
      clashCoreProcess!.exitCode.then((code) => exitCode = code);
      if (exitCode != null && exitCode != 0) {
        controllers.global.updateMsg("启动内核错误,请重启点电脑测试");
        return false;
      }

      controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);

      while (DateTime.now().difference(startTime) < timeout) {
        try {
          controllers.global.updateMsg("等待内核启动..");
          await controllers.core.fetchHello();
          break;
        } catch (_) {
          await Future.delayed(checkInterval);
        }
      }

      if (DateTime.now().difference(startTime) >= timeout) {
        coreStatus.value = RunningState.error;
        controllers.global.updateMsg("内核启动超时,重新点击加速后尝试。");
        return false;
      }
      
      await controllers.core.updateConfig();
      coreStatus.value = RunningState.running;
      controllers.global.updateMsg("点击连接 ");
      return true;
    } catch (e) {
      controllers.global.updateMsg("启动内核错误");
      coreStatus.value = RunningState.error;
      return false;
    }
  }


  Future<void> stopClashCore() async {
    coreStatus.value = RunningState.stopping;
    await controllers.global.closeProxy();
    clashCoreProcess?.kill();
    killProcess(ClashName.name);
    coreStatus.value = RunningState.stoped;
  }

  Future<bool> initClashCoreConfig() async {
    controllers.config.config.value.selected = Files.makeProxyConfig.path;
    //await makeInitConfig();

    await startClashCore();
    if (coreStatus.value == RunningState.error) {
      controllers.global.updateMsg("启动内核失败...");
      return false;
    } else {
      await controllers.core.updateVersion();
      controllers.global.updateMsg("启动内核成功...");
      return true;
    }
  }

  Future<void> stopClash() async {
    controllers.config.config.value.selected = Files.makeInitProxyConfig.path;
    if (coreStatus.value == RunningState.running) {
      await controllers.config.readClashCoreApi();
      controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
      await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
    }
  }

  Future<void> reloadClashCore() async {
    try {
      // if(coreStatus.value == RunningState.stoped){
      //   await updatePorts();
      // }

      controllers.config.config.value.selected = Files.makeProxyConfig.path;
      if (coreStatus.value == RunningState.running) {
        controllers.global.updateMsg("切换配置...");
        await controllers.config.readClashCoreApi();
        controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
        await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
        controllers.global.updateMsg("fetchReloadConfig${controllers.config.clashCoreApiAddress.value}...");
      }
    } catch (e) {
      // if(coreStatus.value == RunningState.stoped){
      //   await updatePorts();
      // }

      controllers.global.updateMsg("重新配置...");
      await controllers.config.readClashCoreApi();
      controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
      await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
    }
  }

  Future<String> generateYamlConfig(List<NodeMode> nodes) async {
   // await updatePorts();
    final config = ClashConfig(
      mixedPort: controllers.config.mixedPort.value,
      allowLan: true,
      bindAddress: '*',
      mode: controllers.global.modesSelect.value == "global" ? "global" : "rule",
      logLevel: 'info',
      externalController: '127.0.0.1:${controllers.config.apiAddressPort.value}',
      unifiedDelay: false,
      geodataMode: true,
      tcpConcurrent: false,
      findProcessMode: 'strict',
      globalClientFingerprint: 'chrome',
      profile: {
        'store-selected': true,
        'store-fake-ip': true,
      },
      sniffer: Sniffer(
        enable: true,
        sniff: {
          'HTTP': {
            'ports': [80, '8080-8880'],
            'override-destination': true,
          },
          'TLS': {
            'ports': [443, 8443],
          },
          // 'QUIC': {
          //   'ports': [443, 8443],
          // },
        },
        skipDomain: ['www.baidu.com'],
      ),
      dns: DNS(
        enable: true,
        listen: kDnsListenPort,
        ipv6: false,
        enhancedMode: kRedirHostMode,
        fakeIpFilter: null,
        nameserver: kDomesticDNS,
        proxyServerNameserver: kDomesticDNS,
        nameserverPolicy: {
          'geosite:cn,private': kDomesticDNS,
          'geosite:geolocation-!cn': kForeignDNS,
        },
      ),
      tun:  Tun(
        enable: controllers.global.routeModesSelect.value == "tun" ? true:false,
        stack: 'gvisor',
        autoRoute: true,
        autoRedirect: false,
        autoDetectInterface: true,
        dnsHijack: ['any:53'],
      ),
      proxies: [],
      proxyGroups: [
        ProxyGroup(
          name: 'proxy',
          type: 'select',
          proxies: [],
        ),
      ],
      rules: [
        'GEOIP,CN,DIRECT',
        'MATCH,proxy',
      ],
    );

    for (final node in nodes) {
      BaseProxy proxy;
      switch (node.type) {
        case 'trojan':
          proxy = TrojanProxy(
            name: node.name ?? '',
            server: node.host ?? '',
            port: node.port ?? 0,
            password: node.passwd ?? '',
            udp: node.udp == 1,
            sni: node.sni,
          );
          break;
        case 'shadowsocks':
          proxy = SSProxy(
            name: node.name ?? '',
            type: 'ss',
            server: node.host ?? '',
            port: node.port ?? 0,
            password: node.passwd ?? '',
            cipher: node.method ?? '',
            udp: node.udp == 1,
          );
          break;
        case 'v2ray':
          final type = (node.vless == 1) ? 'vless' : 'vmess';
          if (type == 'vless') {
            proxy = VlessProxy(
              name: node.name ?? '',
              uuid: node.uuid ?? '',
              server: node.host ?? '',
              port: node.port ?? 0,
              udp: node.udp == 1,
              flow: 'xtls-rprx-vision',
              tls: true,
              servername: node.v2Sni,
              realityOpts: {'public-key': node.vlessPulkey},
              network: 'tcp',
            );
          } else {
            proxy = VmessProxy(
              name: node.name ?? '',
              uuid: node.uuid ?? '',
              server: node.host ?? '',
              port: node.port ?? 0,
              alterId: node.v2AlterId,
              cipher: node.method,
              udp: node.udp == 1,
            );
          }
          break;
        default:
          continue;
      }
      config.proxies?.add(proxy.toJson());
      config.proxyGroups?[0].proxies.add(node.name ?? '');
    }

    return config.toYaml();
  }

  Future<void> saveConfigToFile(String filePath, List<NodeMode> nodes) async {
    try {
      final configYaml = await generateYamlConfig(nodes);
      final file = File(filePath);
      await file.writeAsString(configYaml);
      print('配置文件已成功保存到: $filePath');

    } catch (e) {
      print('保存配置文件时发生错误: $e');
      throw Exception('无法保存配置文件');
    }
  }
}

extension ConfigToYaml on ClashConfig {
  String toYaml() {
    final yamlMap = toJson();
    final yamlEditor = YamlEditor('');
    yamlEditor.update([], yamlMap);
    return yamlEditor.toString();
  }
}