import 'dart:io';
import 'dart:async';
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:naiyouwl/app/bean/ClashServiceInfo.dart';
import 'package:naiyouwl/app/common/LogHelper.dart';
import 'package:naiyouwl/app/const/const.dart';
import 'package:naiyouwl/app/controller/controllers.dart';
import 'package:naiyouwl/app/utils/logger.dart';
import 'package:naiyouwl/app/utils/shell.dart';
import 'package:naiyouwl/app/utils/system_dns.dart';
import 'package:naiyouwl/app/utils/system_proxy.dart';
import 'package:naiyouwl/app/utils/utils.dart';

import 'package:path/path.dart' as path;
import 'package:flutter/foundation.dart';
import 'package:bot_toast/bot_toast.dart';

import 'package:web_socket_channel/io.dart';



final headers = {"User-Agent": "ccore-for-flutter/0.0.1"};

class ServiceController extends GetxController {
  late final _dio = Dio(BaseOptions(baseUrl: 'http://127.0.0.1:9899', headers: headers));
  var serviceMode = false.obs;
  var coreStatus = RunningState.stoped.obs;
  var serviceStatus = RunningState.stoped.obs;

  Process? clashServiceProcess;

  bool get isRunning => serviceStatus.value == RunningState.running && coreStatus.value == RunningState.running;
  bool get isCanOperationService =>
      ![RunningState.starting, RunningState.stopping].contains(serviceStatus.value) &&
      ![RunningState.starting, RunningState.stopping].contains(coreStatus.value);
  bool get isCanOperationCore =>
      serviceStatus.value == RunningState.running && ![RunningState.starting, RunningState.stopping].contains(coreStatus.value);

  ServiceController(

  );

  Future<void> initConfig() async{
    _dio.options.baseUrl = 'http://127.0.0.1:${controllers.config.config.value.servicePort}';
  }

  Future<void> startTunService() async {

    try {
      while (true) {
        final data = await fetchInfo();
        if( data.mode != 'service-mode')
        {
          if (serviceStatus.value == RunningState.running) {
            await stopService();
          }
          await install();
        } else {
          break;
        }
      }

    } catch (e) {

      serviceStatus.value = RunningState.error;
      log.debug(e.toString());
     // BotToast.showText(text: e.toString());
    }

  }
  Future<void> startService() async {
    controllers.global.updateMsg("开启服务");
    serviceStatus.value = RunningState.starting;
    if (Platform.isLinux) {
      await fixBinaryExecutePermissions(Files.assetsClashService);
      await fixBinaryExecutePermissions(Files.assetsClashCore);
    }
    try {
      final data = await fetchInfo();
      serviceMode.value = data.mode == 'service-mode';
      controllers.global.updateMsg("服务模式");
    } catch (e) {
      await startUserModeService();
      controllers.global.updateMsg("开启用户服务");
      if (serviceStatus.value == RunningState.error) return;
    }
    serviceStatus.value = RunningState.running;
  }

  Future<void> fixBinaryExecutePermissions(File file) async {
    final stat = await file.stat();
    // 0b001000000
    final has = (stat.mode & 64) == 64;
    if (has) return;
    await Process.run('chmod', ['+x', file.path]);
  }

  Future<void> startUserModeService() async {
    serviceMode.value = false;
    try {
      int? exitCode;
      clashServiceProcess = await Process.start(Files.assetsClashService.path, ['-port','${controllers.config.config.value.servicePort}','user-mode'], mode: ProcessStartMode.inheritStdio);
      clashServiceProcess!.exitCode.then((code) => exitCode = code);

      while (true) {
        await Future.delayed(const Duration(milliseconds: 200));
        if (exitCode == 101) {
          BotToast.showText(text: 'clash-service exit with code: $exitCode,After 10 seconds, try to restart');
          log.error('After 10 seconds, try to restart');
          await Future.delayed(const Duration(seconds: 10));
          await startUserModeService();
          break;
        } else if (exitCode != null) {
          serviceStatus.value = RunningState.error;
          break;
        }
        try {
          await _dio.post('/info');
          break;
        } catch (_) {}
      }
    } catch (e) {
      serviceStatus.value = RunningState.error;
      BotToast.showText(text: e.toString());
    }
  }

  Future<void> stopService() async {
    serviceStatus.value = RunningState.stopping;
    if (coreStatus.value == RunningState.running) await stopClashCore();
    if (!serviceMode.value) {
      if (clashServiceProcess != null) {
        clashServiceProcess!.kill();
        clashServiceProcess = null;
      } else if (kDebugMode) {
        await killProcess(path.basename(Files.assetsClashService.path));
      }
    }
    serviceStatus.value = RunningState.stoped;
  }

    // for macos
    Future<void> waitServiceStart() async {
      while (true) {
        await Future.delayed(const Duration(milliseconds: 100));
        try {
          await _dio.post('/info');
          break;
        } catch (_) {}
      }
    }

    // for windows
    Future<void> waitServiceStop() async {
      while (true) {
        await Future.delayed(const Duration(milliseconds: 100));
        try {
          await _dio.post('/info');
        } catch (e) {
          break;
        }
      }
    }

    Future<ClashServiceInfo> fetchInfo() async {
      final res = await _dio.post('/info');
      return ClashServiceInfo.fromJson(res.data);
    }

    IOWebSocketChannel fetchLogWs() {
      return IOWebSocketChannel.connect(Uri.parse('ws://127.0.0.1:${controllers.config.config.value.servicePort}/logs'), headers: headers);
    }

    Future<void> fetchStart(String name) async {
      await fetchStop();
      final res = await _dio.post<String>('/start', data: {
        "args": ['-d', Paths.config.path, '-f', path.join(Paths.config.path, name)]
      });
      if (json.decode(res.data!)["code"] != 0) throw json.decode(res.data!)["msg"];
    }

    Future<void> fetchStop() async {
      try {
        await _dio.post('/stop');
      } catch (e) {
        return;
      }
    }

    Future<void> install() async {
      final res = await runAsAdmin(Files.assetsClashService.path, ["-port","${controllers.config.config.value.servicePort}","stop", "uninstall", "install", "start"]);
      await initConfig();
      log.debug('install', res.stdout, res.stderr);
      if (res.exitCode != 0) throw res.stderr;
      await waitServiceStart();
    }

    Future<void> uninstall() async {
      final res = await runAsAdmin(Files.assetsClashService.path, ["stop", "uninstall"]);
      log.debug('uninstall', res.stdout, res.stderr);
      if (res.exitCode != 0) throw res.stderr;
      await waitServiceStop();
    }

    Future<void> serviceModeSwitch(bool open) async {
      if (serviceStatus.value == RunningState.running) await stopService();
      try {
        controllers.global.updateMsg(open ? "安装服务" : "卸载服务");
        open ? await install() : await uninstall();
      } catch (e) {
        BotToast.showText(text: e.toString());
      }
      await startService();
      await startClashCore();
    }

    Future<void> startClashCore() async {
      try {
        //controllers.global.updateMsg("启动内核");
        if( controllers.config.config.value.selected == 'init_proxy.yaml'){
          controllers.global.updateMsg("启动内核初始化");
        } else {
          controllers.global.updateMsg("启动内核");
        }
        coreStatus.value = RunningState.starting;
        await fetchStart(controllers.config.config.value.selected);
        log.debug("api${controllers.config.clashCoreApiAddress.value}");
        controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
        while (true) {
          await Future.delayed(const Duration(milliseconds: 200));
          final info = await fetchInfo();
          if (info.status == 'running') {
            try {
              final ret = await controllers.core.fetchHello();
              log.debug("请求内核结果$ret---服务器状态${info.status}...");
             // controllers.global.updateMsg("请求内核结果$ret---服务器状态${info.status}...");
              break;
            } catch (_) {
              //controllers.global.updateMsg("请求内核错误${e.toString()}---服务器状态${info.status}...");
            }
          } else {
            controllers.global.updateMsg("启动内核错误---等待中...");
            throw '启动内核错误,请检测是否被杀毒拦截,或者端口是否被占用';
          }
        }
        await controllers.core.updateConfig();
        if (Platform.isMacOS &&
            controllers.service.serviceMode.value &&
            controllers.config.clashCoreTunEnable.value &&
            controllers.config.clashCoreDns.isNotEmpty) {
          await MacSystemDns.instance.set([controllers.config.clashCoreDns.value]);
        }
        if (controllers.config.config.value.setSystemProxy) await SystemProxy.instance.set(controllers.core.proxyConfig);
        coreStatus.value = RunningState.running;
        controllers.global.updateMsg("内核状态:${coreStatus.value == RunningState.running} ");
        controllers.global.updateDate();
      } catch (e) {
        log.error("core -- $e");
        controllers.global.updateMsg("启动内核错误");
        //controllers.global.handleApiError(e);
       // BotToast.showText(text: e.toString());
        coreStatus.value = RunningState.error;
      }
    }

    Future<void> stopClashCore() async {
      coreStatus.value = RunningState.stopping;
      if (Platform.isMacOS &&
          controllers.service.serviceMode.value &&
          controllers.config.clashCoreTunEnable.value &&
          controllers.config.clashCoreDns.isNotEmpty) {
        await MacSystemDns.instance.set([]);
      }
      if (controllers.config.config.value.setSystemProxy) await SystemProxy.instance.set(SystemProxyConfig());
      await fetchStop();
      coreStatus.value = RunningState.stoped;
    }
    Future<void> initClashCoreConfig() async {

       //await stopClashCore();
       await startClashCore();
       if (coreStatus.value == RunningState.error) {
         //BotToast.showText(text: '重启失败');
       } else {
         await controllers.core.updateVersion();
         //BotToast.showText(text: '重启成功');
       }
    }

  Future<void> stopClash() async {
    controllers.config.config.value.selected = 'init_proxy.yaml';
    if( coreStatus.value == RunningState.running){
      await controllers.core.fetchReloadConfig({"path":  path.join(Paths.config.path, controllers.config.config.value.selected),"payload": ""});
    }
  }
    Future<void> reloadClashCore() async {
      controllers.config.config.value.selected = 'proxy.yaml';
      // if( coreStatus.value == RunningState.running){
      //   await controllers.core.fetchReloadConfig({"path":  path.join(Paths.config.path, controllers.config.config.value.selected),"payload":""});
      // }
      //BotToast.showText(text: '正在重启 Core ……');
      controllers.global.updateMsg("停止内核...");
      await stopClashCore();
      await controllers.config.readClashCoreApi();
      await startClashCore();
      if (coreStatus.value == RunningState.error) {
        controllers.global.updateMsg("启动内核失败...");
      } else {
        await controllers.core.updateVersion();
        controllers.global.updateMsg("启动内核成功...");
      }
    }

  Future<int> getFreePort() async {
    var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
    int port = server.port;
    await server.close();
    return port;
  }
}
