import 'dart:async';
import 'dart:io';
import 'dart:isolate';

import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'clash/core.dart';
import 'enum/enum.dart';
import 'models/models.dart';
import 'common/common.dart';

class AppController {
  final BuildContext context;
  late AppState appState;
  late Config config;
  late ClashConfig clashConfig;
  late Measure measure;
  late Function updateClashConfigDebounce;

  AppController(this.context) {
    appState = context.read<AppState>();
    config = context.read<Config>();
    clashConfig = context.read<ClashConfig>();
    updateClashConfigDebounce = debounce<Function()>(() async {
      await updateClashConfig();
    });
    measure = Measure.of(context);
  }

  Future<void> updateSystemProxy(bool isStart) async {
    if (isStart) {
      await globalState.startSystemProxy(
        config: config,
        clashConfig: clashConfig,
      );

      updateRunTime();
      updateTraffic();
      globalState.updateFunctionLists = [
        updateRunTime,
        updateTraffic,
      ];
    } else {
      await globalState.stopSystemProxy();
      appState.traffics = [];
      appState.runTime = null;
    }
  }

  updateCoreVersionInfo() {
    globalState.updateCoreVersionInfo(appState);
  }

  updateRunTime() {
    if (proxyManager.startTime != null) {
      final startTimeStamp = proxyManager.startTime!.millisecondsSinceEpoch;
      final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;
      appState.runTime = nowTimeStamp - startTimeStamp;
    } else {
      appState.runTime = null;
    }
  }

  updateTraffic() {
    globalState.updateTraffic(
      config: config,
      appState: appState,
    );
  }

  changeProxy() {
    final currentGroupName =
        appState.getCurrentGroupName(config.currentGroupName, clashConfig.mode);
    final currentProxyName =
        appState.getCurrentProxyName(config.currentProxyName, clashConfig.mode);
    if (config.profiles.isEmpty || currentProxyName == null) {
      updateSystemProxy(false);
      return;
    }
    if (currentGroupName == null) return;
    final groupIndex = appState.groups.indexWhere(
      (element) => element.name == currentGroupName,
    );
    if (groupIndex == -1) return;
    final proxyIndex = appState.groups[groupIndex].all.indexWhere(
      (element) => element.name == currentProxyName,
    );
    if (proxyIndex == -1) return;
    clashCore.changeProxy(
      ChangeProxyParams(
        groupName: currentGroupName,
        proxyName: currentProxyName,
      ),
    );
  }

  addProfile(Profile profile) {
    config.setProfile(profile);
    if (config.currentProfileId != null) return;
    changeProfile(profile.id);
  }

  deleteProfile(String id) async {
    config.deleteProfileById(id);
    final profilePath = await appPath.getProfilePath(id);
    if (profilePath == null) return;
    final file = File(profilePath);
    Isolate.run(() async {
      final isExists = await file.exists();
      if (isExists) {
        file.delete();
      }
    });
    if (config.currentProfileId == id) {
      if (config.profiles.isNotEmpty) {
        final updateId = config.profiles.first.id;
        changeProfile(updateId);
      } else {
        changeProfile(null);
      }
    }
  }

  updateProfile(String id) async {
    final profile = config.getCurrentProfileForId(id);
    if (profile != null) {
      final res = await profile.update();
      if (res.type == ResultType.success) {
        config.setProfile(profile);
      }
    }
  }

  Future<bool> updateClashConfig({bool isPatch = true}) async {
    return await globalState.updateClashConfig(
      clashConfig: clashConfig,
      config: config,
      isPatch: isPatch,
    );
  }

  changeProfile(String? value) async {
    if (value == config.currentProfileId) return;
    config.currentProfileId = value;
    await updateClashConfig(isPatch: false);
    updateGroups();
    changeProxy();
    appState.delayMap = {};
    saveConfigPreferences();
  }

  autoUpdateProfiles() async {
    for (final profile in config.profiles) {
      if (!profile.autoUpdate) return;
      final isNotNeedUpdate = profile.lastUpdateDate
          ?.add(
            profile.autoUpdateDuration,
          )
          .isBeforeNow();
      if (isNotNeedUpdate == false) continue;
      final result = await profile.update();
      if (result.type == ResultType.error) continue;
      updateGroups();
      changeProxy();
    }
  }

  updateGroups() {
    globalState.updateGroups(appState);
  }

  updateSystemColorSchemes(SystemColorSchemes systemColorSchemes) {
    appState.systemColorSchemes = systemColorSchemes;
  }

  clearCurrentDelay() {
    final currentProxyName =
        appState.getCurrentProxyName(config.currentProxyName, clashConfig.mode);
    if (currentProxyName == null) return;
    appState.setDelay(Delay(name: currentProxyName, value: null));
  }

  savePreferences() async {
    await saveConfigPreferences();
    await saveClashConfigPreferences();
  }

  saveConfigPreferences() async {
    debugPrint("saveConfigPreferences");
    await preferences.saveConfig(config);
  }

  saveClashConfigPreferences() async {
    debugPrint("saveClashConfigPreferences");
    await preferences.saveClashConfig(clashConfig);
  }

  handleBackOrExit() async {
    if (config.isMinimizeOnExit) {
      if (system.isDesktop) {
        await savePreferences();
      }
      await system.back();
    } else {
      await handleExit();
    }
  }

  handleExit() async {
    await updateSystemProxy(false);
    await savePreferences();
    clashCore.shutdown();
    system.exit();
  }

  updateLogStatus() {
    if (config.openLogs) {
      clashCore.startLog();
    } else {
      clashCore.stopLog();
      appState.logs = [];
    }
  }

  afterInit() async {
    if (appState.isInit) {
      changeProxy();
      if (config.autoRun) {
        await updateSystemProxy(true);
      } else {
        await proxyManager.updateStartTime();
        await updateSystemProxy(proxyManager.isStart);
      }
      autoUpdateProfiles();
      updateLogStatus();
      if (!config.silentLaunch) {
        window?.show();
      }
    }
  }

  setDelay(Delay delay) {
    appState.setDelay(delay);
  }

  toPage(int index, {bool hasAnimate = false}) {
    final nextLabel = globalState.currentNavigationItems[index].label;
    appState.currentLabel = nextLabel;
    if ((config.isAnimateToPage || hasAnimate)) {
      globalState.pageController?.animateToPage(
        index,
        duration: kTabScrollDuration,
        curve: Curves.easeOut,
      );
    } else {
      globalState.pageController?.jumpToPage(index);
    }
  }

  updatePackages() async {
    await globalState.updatePackages(appState);
  }

  toProfiles() {
    final index = globalState.currentNavigationItems.indexWhere(
      (element) => element.label == "profiles",
    );
    if (index != -1) {
      toPage(index);
    }
  }

  addProfileFormURL(String url) async {
    globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
    toProfiles();
    final commonScaffoldState = globalState.homeScaffoldKey.currentState;
    if (commonScaffoldState?.mounted != true) return;
    commonScaffoldState?.loadingRun(
      () async {
        await Future.delayed(const Duration(milliseconds: 300));
        final profile = Profile(
          url: url,
        );
        final res = await profile.update();
        if (res.type == ResultType.success) {
          addProfile(profile);
        } else {
          debugPrint(res.message);
          globalState.showMessage(
            title: "${appLocalizations.add}${appLocalizations.profile}",
            message: TextSpan(text: res.message!),
          );
        }
      },
    );
  }

  initLink() {
    linkManager.initAppLinksListen(
      (url) {
        globalState.showMessage(
          title: "${appLocalizations.add}${appLocalizations.profile}",
          message: TextSpan(
            children: [
              TextSpan(text: appLocalizations.doYouWantToPass),
              TextSpan(
                text: " $url ",
                style: TextStyle(
                  color: Theme.of(context).colorScheme.primary,
                  decoration: TextDecoration.underline,
                  decorationColor: Theme.of(context).colorScheme.primary,
                ),
              ),
              TextSpan(
                  text:
                      "${appLocalizations.create}${appLocalizations.profile}"),
            ],
          ),
          onTab: () {
            addProfileFormURL(url);
          },
        );
      },
    );
  }

  addProfileFormFile() async {
    final result = await FileUtil.pickerConfig();
    if (result.type == ResultType.error) return;
    if (!context.mounted) return;
    globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
    toProfiles();
    final commonScaffoldState = globalState.homeScaffoldKey.currentState;
    if (commonScaffoldState?.mounted != true) return;
    commonScaffoldState?.loadingRun(
      () async {
        await Future.delayed(const Duration(milliseconds: 300));
        final bytes = result.data?.bytes;
        if (bytes == null) {
          return;
        }
        final profile = Profile(label: result.data?.name);
        final sRes = await profile.saveFile(bytes);
        if (sRes.type == ResultType.error) {
          debugPrint(sRes.message);
          globalState.showMessage(
            title: "${appLocalizations.add}${appLocalizations.profile}",
            message: TextSpan(text: sRes.message!),
          );
          return;
        }
        addProfile(profile);
      },
    );
  }
}
