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

import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

import 'package:get/get.dart';
import 'package:flashlink/app/api/router/api_router.dart';
import 'package:flashlink/app/data/sp/ix_sp.dart';
import 'package:flashlink/app/constants/sp_keys.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:play_install_referrer/play_install_referrer.dart';
import 'package:system_clock/system_clock.dart';
import 'package:uuid/uuid.dart';

import '../../config/translations/localization_service.dart';
import '../../config/translations/strings_enum.dart';
import '../../utils/boost_report_manager.dart';
import '../../utils/gzip_manager.dart';
import '../api/file/api_file.dart';
import '../data/models/banner/banner_list.dart';
import '../data/models/launch/upgrade.dart';
import 'base_core_api.dart';
import '../../utils/api_statistics.dart';
import '../../utils/device_manager.dart';
import '../../utils/geo_downloader.dart';
import '../../utils/log/logger.dart';
import '../../utils/network_helper.dart';

import '../../utils/ntp_time_service.dart';
import '../api/core/api_core.dart';
import '../api/log/api_log.dart';
import '../components/country_restricted_overlay.dart';
import '../components/ix_snackbar.dart';
import '../constants/api_domains.dart';
import '../constants/configs.dart';
import '../constants/enums.dart';
import '../constants/errors.dart';
import '../constants/platforms.dart';
import '../data/models/api_exception.dart';
import '../data/models/channelplan/channel_plan_list.dart';
import '../data/models/failure.dart';
import '../data/models/fingerprint.dart';
import '../data/models/launch/groups.dart';
import '../data/models/launch/launch.dart';
import '../dialog/all_dialog.dart';

class ApiController extends GetxService with WidgetsBindingObserver {
  final TAG = 'ApiController';

  // 记录是否已经显示禁用弹窗
  bool isShowDisabled = false;

  // 上次调用 uploadBoostLog 的时间（用于节流）
  DateTime? _lastUploadBoostLogTime;

  //是否是游客
  final _isGuest = false.obs;
  bool get isGuest => _isGuest.value;
  set isGuest(bool value) => _isGuest.value = value;

  //是否是会员
  final _isPremium = false.obs;
  bool get isPremium => _isPremium.value;
  set isPremium(bool value) => _isPremium.value = value;

  //用户等级
  final _userLevel = 1.obs;
  int get userLevel => _userLevel.value;
  set userLevel(int value) => _userLevel.value = value;

  // 过期时间文本
  final _expireTimeText = ''.obs;
  String get expireTimeText => _expireTimeText.value;
  set expireTimeText(String value) => _expireTimeText.value = value;

  // 有效期文本
  final _validTermText = ''.obs;
  String get validTermText => _validTermText.value;
  set validTermText(String value) => _validTermText.value = value;

  //全部节点列表
  final _nodesList = <LocationList>[].obs;
  List<LocationList> get nodesList => _nodesList.value;
  set nodesList(List<LocationList> value) => _nodesList.value = value;

  //初始化fingerprint
  Fingerprint fp = Fingerprint.empty();

  // 全局剩余时间倒计时（秒）
  int _remainTimeSeconds = 0;
  int get remainTimeSeconds => _remainTimeSeconds;

  // 格式化后的剩余时间字符串（响应式，只有文案变化时才更新 UI）
  final _remainTimeFormatted = ''.obs;
  String get remainTimeFormatted => _remainTimeFormatted.value;

  // 倒计时定时器
  Timer? _remainTimeTimer;

  // 是否在后台
  bool isBackground = false;

  // 切换到后台时的系统时钟时间戳（毫秒，不受系统时间修改影响）
  int _backgroundElapsedRealtime = 0;

  // 记录最新活跃状态时间
  int lastActiveTime = 0;

  @override
  void onInit() {
    super.onInit();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void onClose() {
    WidgetsBinding.instance.removeObserver(this);
    super.onClose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    log(TAG, "App state: $state");
    try {
      if (state == AppLifecycleState.paused) {
        _handleAppPaused();
      } else if (state == AppLifecycleState.resumed) {
        _handleAppResumed();
      }
    } catch (e, s) {
      log(TAG, "didChangeAppLifecycleState error: $e\n$s");
    }
  }

  /// 处理 App 进入后台
  void _handleAppPaused() {
    isBackground = true;
    // 停止倒计时（同步操作，立即执行）
    stopRemainTimeCountdown();

    _backgroundElapsedRealtime = SystemClock.elapsedRealtime().inMilliseconds;

    ApiStatistics.instance.onAppPaused();
  }

  /// 处理 App 回到前台
  Future<void> _handleAppResumed() async {
    if (!isBackground) return;

    isBackground = false;

    // 恢复倒计时（同步操作）
    _resumeRemainTimeCountdown();

    final logs = await ApiStatistics.instance.onAppResumed();
    if (logs.isNotEmpty) {
      uploadLogs(logs);
    }

    // 检查是否需要刷新 launch
    int currentTime = NtpTimeService().getCurrentTimestamp();
    int activeInterval = IXSP.getAppConfig()?.activeInterval ?? 0;
    if (activeInterval > 0 &&
        lastActiveTime + activeInterval * 1000 * 60 > currentTime) {
      return;
    }
    lastActiveTime = currentTime;
    asyncHandleLaunch(isRefreshLaunch: true);
  }

  /// 恢复倒计时（从后台切换到前台时调用）
  void _resumeRemainTimeCountdown() {
    try {
      if (_backgroundElapsedRealtime > 0 && _remainTimeSeconds > 0) {
        // 计算后台经过的秒数（使用系统时钟，不受系统时间修改影响）
        int currentElapsedRealtime;
        try {
          currentElapsedRealtime = SystemClock.elapsedRealtime().inMilliseconds;
        } catch (e) {
          log(TAG, "SystemClock.elapsedRealtime error in resume: $e");
          // 使用备选方案：直接重启倒计时
          startRemainTimeCountdown(_remainTimeSeconds);
          _backgroundElapsedRealtime = 0;
          return;
        }

        final elapsedSeconds =
            (currentElapsedRealtime - _backgroundElapsedRealtime) ~/ 1000;
        // 更新剩余时间
        final newRemainTime = _remainTimeSeconds - elapsedSeconds;
        log(
          TAG,
          'Resume countdown: elapsed=${elapsedSeconds}s, '
          'old=$_remainTimeSeconds, new=$newRemainTime',
        );
        // 重新启动倒计时
        if (newRemainTime > 0) {
          startRemainTimeCountdown(newRemainTime);
        } else {
          // 时间已耗尽
          _remainTimeSeconds = 0;
          _updateRemainTimeFormatted();
          _onRemainTimeExpired();
        }
      }
    } catch (e, s) {
      log(TAG, "_resumeRemainTimeCountdown error: $e\n$s");
    } finally {
      _backgroundElapsedRealtime = 0;
    }
  }

  Future<Fingerprint> initFingerprint() async {
    // 读取app发布渠道
    if (Platform.isIOS) {
      fp.channel = 'apple';
    } else if (Platform.isAndroid) {
      try {
        final channel = await BaseCoreApi().getChannel();
        fp.channel = channel ?? 'unknown';
      } catch (e) {
        log(TAG, 'read app channel error: $e');
        fp.channel = '';
      }
      try {
        final advertisingId = await BaseCoreApi().getAdvertisingId();
        fp.googleId = advertisingId ?? '';
      } catch (e) {
        log(TAG, 'read app googleId error: $e');
        fp.googleId = '';
      }
      try {
        ReferrerDetails referrerDetails =
            await PlayInstallReferrer.installReferrer;
        fp.refer = referrerDetails.installReferrer ?? '';
      } catch (e) {
        log(TAG, 'get install referrer error: $e');
        fp.refer = '';
      }
    } else if (Platform.isWindows) {
      fp.channel = 'universal';
    }

    // 读取应用信息
    final info = await PackageInfo.fromPlatform();
    fp.appVersionCode = int.tryParse(info.buildNumber) ?? 0;
    fp.appVersionName = info.version;
    // 读取设备信息
    final deviceInfo = DeviceInfoPlugin();
    if (Platform.isIOS) {
      fp.platform = Platforms.iOS;
      final iosOsInfo = await deviceInfo.iosInfo;
      fp.deviceModel = iosOsInfo.model;
      fp.deviceOs = iosOsInfo.systemVersion;
      fp.deviceBrand = iosOsInfo.utsname.machine;
    } else if (Platform.isAndroid) {
      fp.platform = Platforms.android;
      final androidOsInfo = await deviceInfo.androidInfo;
      fp.deviceModel = androidOsInfo.model;
      fp.deviceOs = androidOsInfo.version.release;
      fp.deviceBrand = androidOsInfo.brand;
      fp.androidId = androidOsInfo.id;
    } else if (Platform.isWindows) {
      fp.platform = Platforms.windows;
      final windowsInfo = await deviceInfo.windowsInfo;
      fp.deviceModel = windowsInfo.productName;
      fp.deviceOs = windowsInfo.csdVersion;
      fp.deviceBrand = windowsInfo.computerName;
    }
    //获取设备尺寸
    fp.deviceHeight = Get.height.toInt();
    fp.deviceWidth = Get.width.toInt();

    // 读取设备ID
    fp.deviceId = DeviceManager.getCacheDeviceId();
    fp.isNewInstall = IXSP.getIsNewInstall();
    await updateFingerprintData();
    return fp;
  }

  // 更新部分数据
  Future<void> updateFingerprintData() async {
    fp.lang = IXSP.getCurrentLocal().languageCode;
    fp.phoneCountryIso = LocalizationService.getSystemCountry();
    fp.isVpn = await BaseCoreApi().isConnected() ?? false;
    if (!fp.isVpn) {
      fp.isConnectedVpn = false;
    }
    try {
      if (!Platform.isWindows) {
        final simInfo = await BaseCoreApi().getSimInfo();
        // 解析sim
        final sim = jsonDecode(simInfo ?? '{}');
        fp.simReady = sim['simReady'];
        fp.carrierName = sim['carrierName'];
        fp.mcc = sim['mcc'];
        fp.mnc = sim['mnc'];
        fp.countryIso = sim['countryIso'];
        fp.networkCarrierName = sim['networkCarrierName'];
        fp.networkMcc = sim['networkMcc'];
        fp.networkMnc = sim['networkMnc'];
        fp.networkCountryIso = sim['networkCountryIso'];
      }
    } catch (e) {
      log(TAG, 'read app sim error: $e');
    }
  }

  Future<void> initData(Launch? launch) async {
    // 初始化是否第一次安装
    IXSP.setIsNewInstall(false);
    fp.userUuid = '';
    fp.isNewInstall = false;
    await initLaunch(launch);
  }

  Future<void> initLaunch(Launch? launch) async {
    try {
      if (launch != null) {
        // 初始化用户状态
        isGuest = launch.userConfig?.memberLevel == MemberLevel.guest.level;
        userLevel = launch.userConfig?.userLevel ?? 1;
        isPremium = userLevel == 3 || userLevel == 9999;
        expireTimeText = _getExpireTimeText();
        validTermText = _getValidTermText();
        updateRemainTime(launch.userConfig?.remainTime ?? 0);

        NtpTimeService().initLaunchInitialTime();
        // 设置路由和节点
        nodesList = launch.groups?.normal?.list ?? [];

        // 设置资源url
        if (launch.appConfig?.assetUrls != null &&
            launch.appConfig!.assetUrls!.isNotEmpty) {
          Configs.assetUrl = launch.appConfig!.assetUrls![0];
        }

        // 设置官网url
        if (launch.appConfig?.websiteUrl != null &&
            launch.appConfig!.websiteUrl!.isNotEmpty) {
          Configs.websiteUrl = launch.appConfig!.websiteUrl!;
        }
      }
    } catch (e) {
      log(TAG, 'initLaunch error: $e');
    }
  }

  // 发送分析事件, 后续可以发送到firebase
  Future<void> sendAnalytics(FirebaseEvent event) async {
    try {} catch (e) {
      log('sendAnalytics error: $e');
    }
  }

  Future<Launch> launch({bool isCache = false}) async {
    sendAnalytics(isCache ? FirebaseEvent.launchCache : FirebaseEvent.launch);
    while (true) {
      try {
        ApiCore().setbaseUrl(ApiDomains.instance.getApiUrl());
        final request = fp.toJson();
        final result = await ApiCore().launch(request);

        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        sendAnalytics(
          isCache
              ? FirebaseEvent.launchCacheSuccess
              : FirebaseEvent.launchSuccess,
        );
        // 重置禁用状态
        IXSP.setLastIsRegionDisabled(false);
        IXSP.setLastIsUserDisabled(false);

        final launchData = Launch.fromJson(result.data);

        // 设置扩展数据
        fp.exData = launchData.exData;

        // 更新URL列表
        await ApiDomains.instance.updateFromLaunch(launchData);

        // 保存Launch数据
        await IXSP.saveLaunch(launchData);

        // 初始化Launch
        await initData(launchData);

        // 缓存日志上传
        processCachedLogs();

        // 缓存文件日志上传
        processCachedFileLogs();

        return launchData;
      } on ApiException catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'Launch request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextApiUrl();
            log(TAG, 'Launch request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiCore().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'Launch request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      }
    }
  }

  Future<Launch> refreshLaunch() async {
    while (true) {
      try {
        ApiCore().setbaseUrl(ApiDomains.instance.getApiUrl());
        final request = fp.toJson();
        final result = await ApiCore().refreshLaunch(request);

        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        // 重置禁用状态
        IXSP.setLastIsRegionDisabled(false);
        IXSP.setLastIsUserDisabled(false);

        final launchData = Launch.fromJson(result.data);

        // 设置扩展数据
        fp.exData = launchData.exData;

        // 更新URL列表
        await ApiDomains.instance.updateFromLaunch(launchData);

        // 保存Launch数据
        await IXSP.saveLaunch(launchData);

        // 初始化Launch
        await initData(launchData);

        // 缓存日志上传
        processCachedLogs();

        // 缓存文件日志上传
        processCachedFileLogs();

        return launchData;
      } on ApiException catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'refresh launch request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextApiUrl();
            log(TAG, 'refresh launch request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiCore().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'refresh launch request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      }
    }
  }

  Future<void> asyncHandleLaunch({bool isRefreshLaunch = false}) async {
    try {
      final data = isRefreshLaunch
          ? await refreshLaunch()
          : await launch(isCache: true);
      final isVpnRunning = await BaseCoreApi().isConnected() ?? false;
      if (!isVpnRunning) {
        await checkUpdate();
        // 下载smartgeo文件
        GeoDownloader().downloadSmartGeo(smartGeo: data.appConfig!.smartGeo!);
      }
    } catch (e, s) {
      if (IXSP.getLastIsUserDisabled()) {
        if (!isShowDisabled) {
          Get.offAll(
            () => CountryRestrictedOverlay(
              type: RestrictedType.user,
              onPressed: () async {
                // 清除LaunchData
                await IXSP.clearLaunchData();
                // 清除禁用状态
                IXSP.setLastIsUserDisabled(false);
                // 发送事件
              },
            ),
            transition: Transition.fadeIn,
          );
        }
        return;
      } else if (IXSP.getLastIsRegionDisabled()) {
        if (!isShowDisabled) {
          Get.offAll(
            () => const CountryRestrictedOverlay(type: RestrictedType.region),
            transition: Transition.fadeIn,
          );
        }
        return;
      } else if (IXSP.getLastIsDeviceDisabled()) {
        if (!isShowDisabled) {
          Get.offAll(
            () => const CountryRestrictedOverlay(type: RestrictedType.device),
            transition: Transition.fadeIn,
          );
        }
        return;
      }
      final isVpnRunning = await BaseCoreApi().isConnected() ?? false;
      if (!isVpnRunning) {
        await checkUpdate();
      }
      handleSnackBarError(e, s);
    }
  }

  void handleSnackBarError(dynamic error, StackTrace stackTrace) {
    if (error is ApiException) {
      IXSnackBar.showIXErrorSnackBar(
        title: Strings.error.tr,
        message: error.message,
      );
    } else if (error is Failure) {
      IXSnackBar.showIXErrorSnackBar(
        title: Strings.error.tr,
        message: error.message ?? Strings.unknownError.tr,
      );
    } else if (error is DioException) {
      switch (error.type) {
        case DioExceptionType.connectionError:
        case DioExceptionType.connectionTimeout:
        case DioExceptionType.receiveTimeout:
        case DioExceptionType.sendTimeout:
          IXSnackBar.showIXErrorSnackBar(
            title: Strings.error.tr,
            message: Strings.unableToConnectNetwork.tr,
          );
          break;
        default:
          IXSnackBar.showIXErrorSnackBar(
            title: Strings.error.tr,
            message: Strings.unableToConnectServer.tr,
          );
      }
    } else {
      IXSnackBar.showIXErrorSnackBar(
        title: Strings.error.tr,
        message: error.toString(),
      );
    }
  }

  // 更新检查 - 智能时间控制版本
  Future<bool> checkUpdate({bool isClickCheck = false}) async {
    try {
      final upgrade = IXSP.getUpgrade();
      // 如果当前versionCode等于升级的versionCode，则没有更新
      if (upgrade?.versionCode == fp.appVersionCode) {
        return false;
      }

      var hasUpdate = false;
      var hasForceUpdate = false;

      if (upgrade != null) {
        if (upgrade.upgradeType == 1) {
          hasUpdate = true;
        }
        if (upgrade.forced == true) {
          hasForceUpdate = true;
        }
      }

      if (hasUpdate) {
        // 强制更新或用户主动点击检查时，直接显示更新弹窗
        if (hasForceUpdate || isClickCheck) {
          _showUpgradeDialog(upgrade!, hasForceUpdate);
          return hasUpdate;
        }

        // 检查版本是否变化（新版本则重置时间逻辑）
        final currentVersion = upgrade?.versionCode?.toString() ?? '';
        final lastNoticeVersion =
            IXSP.getString(SPKeys.lastUpgradeNoticeVersion) ?? '';
        final isNewVersion = currentVersion != lastNoticeVersion;

        if (isNewVersion) {
          // 新版本，直接显示弹窗
          log(
            TAG,
            'New version detected: $currentVersion (last: $lastNoticeVersion)',
          );
          _showUpgradeDialog(upgrade!, hasForceUpdate);
          return hasUpdate;
        }

        // 检查是否超过 upgradeNoticeTime 分钟
        final appConfig = IXSP.getAppConfig();
        final upgradeNoticeMinutes =
            appConfig?.upgradeNoticeTime ?? 1440; // 默认1天
        final lastNoticeTimeStr =
            IXSP.getString(SPKeys.lastUpgradeNoticeTime) ?? '0';
        final lastNoticeTime = int.tryParse(lastNoticeTimeStr) ?? 0;
        final now = NtpTimeService().getCurrentTimestamp();
        final elapsedMinutes = (now - lastNoticeTime) / (1000 * 60);

        if (elapsedMinutes >= upgradeNoticeMinutes) {
          _showUpgradeDialog(upgrade!, hasForceUpdate);
        } else {
          log(
            TAG,
            'Upgrade notice skipped: ${elapsedMinutes.toStringAsFixed(1)}min '
            'elapsed, need ${upgradeNoticeMinutes}min',
          );
        }
      }
      return hasUpdate;
    } catch (e) {
      log(TAG, 'checkUpdate error: $e');
    }
    return false;
  }

  /// 显示更新弹窗并记录时间和版本
  void _showUpgradeDialog(Upgrade upgrade, bool hasForceUpdate) {
    AllDialog.showUpdate(upgrade, hasForceUpdate: hasForceUpdate);
    // 记录本次提醒时间
    IXSP.setString(
      SPKeys.lastUpgradeNoticeTime,
      NtpTimeService().getCurrentTimestamp().toString(),
    );
    // 记录本次提醒的版本号
    IXSP.setString(
      SPKeys.lastUpgradeNoticeVersion,
      upgrade.versionCode?.toString() ?? '',
    );
  }

  Future<Launch> getDispatchInfo(
    int locationId,
    String locationCode, {
    CancelToken? cancelToken,
  }) async {
    while (true) {
      try {
        ApiRouter().setbaseUrl(ApiDomains.instance.getRouterUrl());
        final request = fp.toJson();
        request['locationId'] = locationId;
        request['locationCode'] = locationCode;
        // 获取选中的路由模式
        final routingMode =
            IXSP.getString(SPKeys.routingModeSelected) ?? "smart";
        request['clientConfig'] = {'routingMode': routingMode};
        final result = await ApiRouter().getDispatchInfo(
          request,
          cancelToken: cancelToken,
        );

        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        // 重置禁用状态
        IXSP.setLastIsRegionDisabled(false);
        IXSP.setLastIsUserDisabled(false);

        final launchData = Launch.fromJson(result.data);

        // 更新URL列表
        await ApiDomains.instance.updateFromLaunch(launchData);

        // 保存app配置
        await IXSP.saveAppConfig(launchData.appConfig!);

        return launchData;
      } on ApiException catch (_) {
        rethrow;
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextRouterUrl();
            log(TAG, 'getDispatchInfo request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiRouter().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextRouterUrl();
        log(TAG, 'getDispatchInfo request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiRouter().setbaseUrl(url);
      }
    }
  }

  Future<Launch> register(Map<String, dynamic> params) async {
    while (true) {
      try {
        ApiCore().setbaseUrl(ApiDomains.instance.getApiUrl());
        final request = fp.toJson();
        request.addAll(params);
        final result = await ApiCore().register(request);
        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        final launchData = Launch.fromJson(result.data);

        // 注册成功后上报firebase注册事件
        sendAnalytics(FirebaseEvent.register);

        // 保存 Launch 数据
        await IXSP.saveLaunch(launchData);

        // 初始化Launch
        await initData(launchData);

        return launchData;
      } on ApiException catch (_) {
        rethrow;
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextApiUrl();
            log(TAG, 'Register request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiCore().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'Register request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      }
    }
  }

  Future<Launch> login(Map<String, dynamic> params) async {
    while (true) {
      try {
        ApiCore().setbaseUrl(ApiDomains.instance.getApiUrl());
        final request = fp.toJson();
        request.addAll(params);
        final result = await ApiCore().login(request);
        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        final launchData = Launch.fromJson(result.data);

        // 注册成功后上报firebase注册事件
        sendAnalytics(FirebaseEvent.login);

        // 保存 Launch 数据
        await IXSP.saveLaunch(launchData);

        // 初始化Launch
        await initData(launchData);

        return launchData;
      } on ApiException catch (_) {
        rethrow;
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextApiUrl();
            log(TAG, 'Login request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiCore().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'Login request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      }
    }
  }

  Future<Launch> logout() async {
    try {
      final request = fp.toJson();
      final result = await ApiCore().logout(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final launchData = Launch.fromJson(result.data);

      // 登出成功后上报firebase登出事件
      sendAnalytics(FirebaseEvent.logout);

      // 保存 Launch 数据
      await IXSP.saveLaunch(launchData);

      await initData(launchData);
      return launchData;
    } catch (e) {
      rethrow;
    }
  }

  Future<Launch> deleteAccount() async {
    try {
      final request = fp.toJson();
      final result = await ApiCore().deleteAccount(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final launchData = Launch.fromJson(result.data);

      // 登出成功后上报firebase登出事件
      sendAnalytics(FirebaseEvent.deleteAccount);

      // 保存 Launch 数据
      await IXSP.saveLaunch(launchData);

      await initData(launchData);
      return launchData;
    } catch (e) {
      rethrow;
    }
  }

  Future<String> changePassword(Map<String, dynamic> params) async {
    try {
      final request = fp.toJson();
      request.addAll(params);
      final result = await ApiCore().changePassword(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      return result.errorMessage ?? '';
    } catch (e) {
      rethrow;
    }
  }

  Future<Groups> getLocations() async {
    while (true) {
      try {
        ApiCore().setbaseUrl(ApiDomains.instance.getApiUrl());
        final request = fp.toJson();
        final result = await ApiCore().getLocations(request);
        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        final groups = Groups.fromJson(result.data);
        await IXSP.saveGroups(groups);
        return groups;
      } on ApiException catch (_) {
        rethrow;
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextApiUrl();
            log(TAG, 'getLocations request failed for URL $url: $e');
            if (url.isEmpty) {
              rethrow;
            }
            ApiCore().setbaseUrl(url);
          } else {
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextApiUrl();
        log(TAG, 'getLocations request failed for URL $url: $e');
        if (url.isEmpty) {
          rethrow;
        }
        ApiCore().setbaseUrl(url);
      }
    }
  }

  Future<List<ChannelPlan>> getChannelPlanList() async {
    try {
      final request = fp.toJson();
      final result = await ApiCore().getChannelPlanList(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final channelPlanList = ChannelPlanList.fromJson(result.data);
      return channelPlanList.list ?? [];
    } catch (e) {
      rethrow;
    }
  }

  Future<Launch> subscribe(Map<String, dynamic> params) async {
    try {
      final request = fp.toJson();
      request.addAll(params);
      final result = await ApiCore().subscribe(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final launchData = Launch.fromJson(result.data);

      // 登出成功后上报firebase登出事件
      sendAnalytics(FirebaseEvent.subscribe);

      // 保存 Launch 数据
      await IXSP.saveLaunch(launchData);
      // 初始化Launch
      await initData(launchData);
      return launchData;
    } catch (e) {
      rethrow;
    }
  }

  Future<Launch> restore() async {
    try {
      final request = fp.toJson();
      final result = await ApiCore().restore(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final launchData = Launch.fromJson(result.data);

      // 登出成功后上报firebase登出事件
      sendAnalytics(FirebaseEvent.restore);

      // 保存 Launch 数据
      await IXSP.saveLaunch(launchData);
      // 初始化Launch
      await initData(launchData);
      return launchData;
    } catch (e) {
      rethrow;
    }
  }

  Future<void> connected(Map<String, dynamic> params) async {
    try {
      final request = fp.toJson();
      request.addAll(params);
      final result = await ApiRouter().connected(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
    } catch (e) {
      rethrow;
    }
  }

  Future<BannerList> getBanner({String position = "banner"}) async {
    try {
      final request = fp.toJson();
      request["position"] = position;
      final result = await ApiCore().getBanner(request);
      if (!result.success) {
        throw Failure(
          code: result.errorCode ?? '',
          message: result.errorMessage ?? '',
        );
      }
      final bannerList = BannerList.fromJson(result.data);
      return bannerList;
    } catch (e) {
      rethrow;
    }
  }

  /// 上传 Boost 日志（读取 app.json 和 core.json 并组合上传）
  /// 1秒内只允许调用一次
  Future<void> uploadBoostLog() async {
    // 节流检查：1秒内只允许调用一次
    final now = DateTime.now();
    if (_lastUploadBoostLogTime != null &&
        now.difference(_lastUploadBoostLogTime!).inMilliseconds < 1000) {
      log(TAG, 'uploadBoostLog throttled, skip');
      return;
    }
    _lastUploadBoostLogTime = now;

    try {
      // 读取 app.json 内容
      Map<String, dynamic> appLog = {};
      final appLogPath = await BoostReportManager().getAppLogFilePath();
      if (appLogPath != null) {
        final appFile = File(appLogPath);
        if (await appFile.exists()) {
          final content = await appFile.readAsString();
          if (content.isNotEmpty) {
            appLog = jsonDecode(content) as Map<String, dynamic>;
          }
        }
      }

      // 读取 core.json 内容
      Map<String, dynamic> coreLog = {};
      final coreLogPath = await BoostReportManager().getCoreLogFilePath();
      if (coreLogPath != null) {
        final coreFile = File(coreLogPath);
        if (await coreFile.exists()) {
          final content = await coreFile.readAsString();
          if (content.isNotEmpty) {
            coreLog = jsonDecode(content) as Map<String, dynamic>;
          }
        }
      }

      // 如果两个日志都为空，则不上传
      if (appLog.isEmpty && coreLog.isEmpty) {
        log(TAG, 'Both app log and core log are empty, skip upload');
        return;
      }

      // 组装日志数据
      final logItem = {
        'id': const Uuid().v4(),
        'time': DateTime.now().millisecondsSinceEpoch,
        'level': LogLevel.info.name,
        'module': LogModule.NM_Metrics.name,
        'category': Configs.productCode,
        'fields': {'appLog': appLog, 'coreLog': coreLog},
      };

      // 上传日志
      try {
        await uploadMetricsLog([logItem]);
      } catch (e) {
        log(TAG, 'uploadMetricsLog error: $e');
      }
      try {
        final boostSessionId = appLog['sessionInfo']?['boostSessionId'] ?? '';
        if (boostSessionId.isEmpty) {
          return;
        }
        final errorCode = appLog['sessionInfo']?['errorCode'] ?? -1;
        if (errorCode <= 0) {
          return;
        }
        await uploadLocalLog(boostSessionId);
      } catch (e) {
        log(TAG, 'uploadLocalLog error: $e');
      }
    } catch (e) {
      log(TAG, 'uploadBoostLog error: $e');
    }
  }

  Future<void> uploadMetricsLog(List<Map<String, dynamic>> logs) async {
    try {
      await uploadLogs(logs, isCache: true);
    } catch (e) {
      log(TAG, 'uploadMetricsLog error: $e');
    }
  }

  Future<void> uploadLocalLog(String boostSessionId) async {
    try {
      final appDir = await getApplicationSupportDirectory();
      final logDir = Directory('${appDir.path}/logs');
      final xrayDir = Directory('${appDir.path}/xray');
      final filePaths = <String>[];
      if (await logDir.exists()) {
        // 遍历 logDir 目录，查找 client_ 和 service_ 开头的文件
        await for (final entity in logDir.list()) {
          if (entity is File) {
            final fileName = entity.path.split('/').last;
            if (fileName.startsWith('client_') ||
                fileName.startsWith('service_')) {
              filePaths.add(entity.path);
            }
          }
        }
      }
      if (await xrayDir.exists()) {
        // 遍历 logDir 目录，查找 client_ 和 service_ 开头的文件
        await for (final entity in xrayDir.list()) {
          if (entity is File) {
            filePaths.add(entity.path);
          }
        }
      }
      if (filePaths.isEmpty) {
        log(TAG, 'No client_ or service_ log files found');
        return;
      }
      final gzipFilePath = await GzipManager().generateAndShareGzip(
        filePaths: filePaths,
        gzipFileName: '$boostSessionId.tar.gz',
      );
      if (gzipFilePath == null) {
        return;
      }
      await uploadLogFile(gzipFilePath, boostSessionId, LogModule.NM_Log.name);
    } catch (e) {
      rethrow;
    }
  }

  Future<String> uploadLogs(List<dynamic> items, {bool isCache = false}) async {
    await updateFingerprintData();
    Map<String, dynamic> request = fp.toJson();
    request['items'] = items;
    while (true) {
      try {
        ApiLog().setbaseUrl(ApiDomains.instance.getLogUrl());
        final result = await ApiLog().uploadLogs(request);
        if (!result.success) {
          throw Failure(
            code: result.errorCode ?? '',
            message: result.errorMessage ?? '',
          );
        }
        return result.errorMessage ?? '';
      } on ApiException catch (_) {
        rethrow;
      } on Failure catch (_) {
        rethrow;
      } on DioException catch (e) {
        if (e.response?.statusCode == Errors.eRegionNotAvailable ||
            e.response?.statusCode == Errors.eUserDisabled ||
            e.response?.statusCode == Errors.eTokenExpired) {
          if (isCache) {
            await _cacheLogRequest(items);
          }
          rethrow;
        } else {
          if (await NetworkHelper.instance.isNetworkAvailable()) {
            final url = await ApiDomains.instance.getNextLogUrl();
            if (url.isEmpty) {
              if (isCache) {
                await _cacheLogRequest(items);
              }
              rethrow;
            }
            log(
              TAG,
              'uploadLogs request failed for URL ${ApiLog().baseUrl}: $e',
            );
            ApiLog().setbaseUrl(url);
          } else {
            if (isCache) {
              await _cacheLogRequest(items);
            }
            rethrow;
          }
        }
      } catch (e) {
        final url = await ApiDomains.instance.getNextLogUrl();
        if (url.isEmpty) {
          if (isCache) {
            await _cacheLogRequest(items);
          }
          rethrow;
        }
        log(TAG, 'uploadLogs request failed for URL ${ApiLog().baseUrl}: $e');
        ApiLog().setbaseUrl(url);
      }
    }
  }

  // 上传文件
  Future<String> uploadLogFile(
    String filePath,
    String fileName,
    String logType, {
    bool isCache = false,
  }) async {
    try {
      Map<String, dynamic> request = fp.toJson();
      final data = jsonEncode(request);
      while (true) {
        try {
          ApiFile().setbaseUrl(ApiDomains.instance.getFileUrl());
          final result = await ApiFile().uploadLogFile(
            filePath,
            fileName,
            logType,
            data,
          );
          if (!result.success) {
            throw Failure(
              code: result.errorCode ?? '',
              message: result.errorMessage ?? '',
            );
          }
          return result.errorMessage ?? '';
        } on ApiException catch (_) {
          rethrow;
        } on Failure catch (_) {
          rethrow;
        } on DioException catch (e) {
          if (e.response?.statusCode == Errors.eRegionNotAvailable ||
              e.response?.statusCode == Errors.eUserDisabled ||
              e.response?.statusCode == Errors.eTokenExpired) {
            if (isCache) {
              await _cacheFileLogRequest(filePath, fileName);
            }
            rethrow;
          } else {
            if (await NetworkHelper.instance.isNetworkAvailable()) {
              final url = await ApiDomains.instance.getNextFileUrl();
              if (url.isEmpty) {
                if (isCache) {
                  await _cacheFileLogRequest(filePath, fileName);
                }
                rethrow;
              }
              log('uploadLogs request failed for URL ${ApiLog().baseUrl}: $e');
              ApiLog().setbaseUrl(url);
            } else {
              if (isCache) {
                await _cacheFileLogRequest(filePath, fileName);
              }
              rethrow;
            }
          }
        } catch (e) {
          final url = await ApiDomains.instance.getNextFileUrl();
          if (url.isEmpty) {
            if (isCache) {
              await _cacheFileLogRequest(filePath, fileName);
            }
            rethrow;
          }
          log('uploadLogs request failed for URL ${ApiLog().baseUrl}: $e');
          ApiLog().setbaseUrl(url);
        }
      }
    } catch (e) {
      rethrow;
    }
  }

  // 缓存文件日志请求数据
  Future<void> _cacheFileLogRequest(String filePath, String fileName) async {
    try {
      final dir = await getApplicationSupportDirectory();
      final cacheDir = Directory('${dir.path}/file_log_cache');
      if (!await cacheDir.exists()) {
        await cacheDir.create(recursive: true);
      }
      // 复制文件到缓存目录
      final file = File(filePath);
      await file.copy('${cacheDir.path}/${file.path.split('/').last}');
      // 清理缓存目录
      await _cleanupFileCacheDirectory(cacheDir);
    } catch (e) {
      log('Failed to cache file log request: $e');
    }
  }

  // 清理文件缓存目录
  Future<void> _cleanupFileCacheDirectory(Directory cacheDir) async {
    try {
      const maxFiles = 10; // 最多保留10个文件
      final files = await cacheDir.list().toList();

      // 按修改时间排序
      files.sort(
        (a, b) => a.statSync().modified.compareTo(b.statSync().modified),
      );

      // 如果超过文件数量或大小限制，删除最旧的文件
      while (files.length > maxFiles && files.isNotEmpty) {
        final oldestFile = files.removeAt(0);
        await oldestFile.delete();
      }
    } catch (e) {
      log('Failed to cleanup cache directory: $e');
    }
  }

  // 处理缓存的文件日志数据
  Future<void> processCachedFileLogs() async {
    try {
      final dir = await getApplicationSupportDirectory();
      final cacheDir = Directory('${dir.path}/file_log_cache');
      if (!await cacheDir.exists()) {
        return;
      }

      final files = await cacheDir.list().toList();
      if (files.isEmpty) {
        return;
      }

      // 按修改时间排序，先处理最旧的文件
      files.sort(
        (a, b) => a.statSync().modified.compareTo(b.statSync().modified),
      );

      for (var fileEntity in files) {
        try {
          if (fileEntity is! File) continue;
          final filePath = fileEntity.path;
          final fileName = fileEntity.path.split('/').last.split('.').first;
          // 尝试上传缓存的日志
          await uploadLogFile(filePath, fileName, LogModule.NM_Log.name);

          // 上传成功后删除缓存文件
          await fileEntity.delete();
        } catch (e) {
          log('Failed to process cached log file ${fileEntity.path}: $e');
          // 如果上传失败，保留文件等待下次尝试
          continue;
        }
      }
    } catch (e) {
      log('Failed to process cached file logs: $e');
    }
  }

  // 缓存日志请求数据
  Future<void> _cacheLogRequest(dynamic items) async {
    try {
      final dir = await getApplicationSupportDirectory();
      final cacheDir = Directory('${dir.path}/log_cache');
      if (!await cacheDir.exists()) {
        await cacheDir.create(recursive: true);
      }

      // 生成唯一的文件名
      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final fileName = 'log_$timestamp.json';
      final file = File('${cacheDir.path}/$fileName');

      // 将请求数据写入文件
      await file.writeAsString(jsonEncode(items));

      // 清理缓存目录
      await _cleanupCacheDirectory(cacheDir);
    } catch (e) {
      log(TAG, 'Failed to cache log request: $e');
    }
  }

  // 清理缓存目录
  Future<void> _cleanupCacheDirectory(Directory cacheDir) async {
    try {
      const maxFiles = 10; // 最多保留10个文件
      const maxCacheSize = 5 * 1024 * 1024; // 5MB
      final files = await cacheDir.list().toList();

      // 按修改时间排序
      files.sort(
        (a, b) => a.statSync().modified.compareTo(b.statSync().modified),
      );

      // 计算当前缓存大小
      int totalSize = 0;
      for (var file in files) {
        totalSize += file.statSync().size;
      }

      // 如果超过文件数量或大小限制，删除最旧的文件
      while ((files.length > maxFiles || totalSize > maxCacheSize) &&
          files.isNotEmpty) {
        final oldestFile = files.removeAt(0);
        totalSize -= oldestFile.statSync().size;
        await oldestFile.delete();
      }
    } catch (e) {
      log(TAG, 'Failed to cleanup cache directory: $e');
    }
  }

  // 处理缓存的日志数据
  Future<void> processCachedLogs() async {
    try {
      final dir = await getApplicationSupportDirectory();
      final cacheDir = Directory('${dir.path}/log_cache');
      if (!await cacheDir.exists()) {
        return;
      }

      final files = await cacheDir.list().toList();
      if (files.isEmpty) {
        return;
      }

      // 按修改时间排序，先处理最旧的文件
      files.sort(
        (a, b) => a.statSync().modified.compareTo(b.statSync().modified),
      );

      for (var fileEntity in files) {
        try {
          if (fileEntity is! File) continue;
          final content = await fileEntity.readAsString();
          final items = jsonDecode(content);

          // 尝试上传缓存的日志
          await uploadLogs(items, isCache: false);

          // 上传成功后删除缓存文件
          await fileEntity.delete();
        } catch (e) {
          log('Failed to process cached log file ${fileEntity.path}: $e');
          // 如果上传失败，保留文件等待下次尝试
          continue;
        }
      }
    } catch (e) {
      log('Failed to process cached logs: $e');
    }
  }

  /// 获取订阅周期类型文本
  /// subscribeType: 1Day 2Week 3Month 4Year
  // String _getSubscribeTypeText() {
  //   final user = IXSP.getUser();
  //   final planInfo = user?.planInfo;

  //   // 仅当 isSubscribe=true 时有效
  //   if (planInfo?.isSubscribe != true) {
  //     return planInfo?.subTitle ?? '';
  //   }

  //   switch (planInfo?.subscribeType) {
  //     case 1:
  //       return 'Day';
  //     case 2:
  //       return 'Week';
  //     case 3:
  //       return 'Month';
  //     case 4:
  //       return 'Year';
  //     default:
  //       return '';
  //   }
  // }

  /// 获取过期时间文本
  String _getExpireTimeText() {
    final user = IXSP.getUser();
    final expireTime = user?.expireTime;

    if (expireTime == null || expireTime == 0) {
      return '';
    }

    // 时间戳转日期（秒级时间戳）
    final date = DateTime.fromMillisecondsSinceEpoch(expireTime * 1000);
    final formatted =
        "${date.year.toString().padLeft(4, '0')}-"
        "${date.month.toString().padLeft(2, '0')}-"
        "${date.day.toString().padLeft(2, '0')}";
    return formatted;
  }

  /// 获取有效期显示文本
  String _getValidTermText() {
    // final subscribeType = _getSubscribeTypeText();
    final expireTime = _getExpireTimeText();

    // if (subscribeType.isNotEmpty && expireTime.isNotEmpty) {
    //   return '$subscribeType / $expireTime';
    // }
    if (expireTime.isNotEmpty) {
      return expireTime;
    }
    return '';
  }

  /// 启动剩余时间倒计时
  /// [seconds] 剩余时间（秒），例如：3600 表示 1 小时
  void startRemainTimeCountdown(int seconds) {
    // 取消之前的定时器
    _remainTimeTimer?.cancel();

    // 设置初始剩余时间
    _remainTimeSeconds = seconds;
    // 立即更新格式化文案
    _updateRemainTimeFormatted();

    // 启动每秒倒计时
    _remainTimeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (_remainTimeSeconds > 0) {
        _remainTimeSeconds--;
        // 只有当格式化文案变化时才更新 UI
        _updateRemainTimeFormatted();
      } else {
        // 倒计时结束
        timer.cancel();
        _remainTimeTimer = null;
        _onRemainTimeExpired();
      }
    });
  }

  /// 更新格式化后的剩余时间（只有文案变化时才触发 UI 更新）
  void _updateRemainTimeFormatted() {
    final newFormatted = _formatRemainTime(_remainTimeSeconds);
    if (_remainTimeFormatted.value != newFormatted) {
      _remainTimeFormatted.value = newFormatted;
    }
  }

  /// 停止剩余时间倒计时
  void stopRemainTimeCountdown() {
    _remainTimeTimer?.cancel();
    _remainTimeTimer = null;
  }

  /// 更新剩余时间（从服务器获取新的时间后调用）
  void updateRemainTime(int seconds) {
    stopRemainTimeCountdown();
    if (seconds > 0) {
      startRemainTimeCountdown(seconds);
    } else {
      _remainTimeSeconds = 0;
      _updateRemainTimeFormatted();
    }
  }

  /// 剩余时间到期处理
  void _onRemainTimeExpired() {
    log(TAG, 'VIP剩余时间已到期');
    // 可以在这里添加到期后的处理逻辑，例如：
    // - 显示续费提示
    // - 断开VPN连接
    // - 刷新用户信息
    refreshLaunch();
  }

  /// 格式化剩余时间显示
  String _formatRemainTime(int totalSeconds) {
    if (totalSeconds <= 0) {
      return Strings.expired.tr;
    }

    final days = totalSeconds ~/ 86400;
    final hours = (totalSeconds % 86400) ~/ 3600;
    final minutes = (totalSeconds % 3600) ~/ 60;
    final seconds = totalSeconds % 60;

    // 大于1天
    if (days > 1) {
      return '$days ${Strings.days.tr}';
    }
    // 等于1天
    if (days == 1) {
      return '1 ${Strings.day.tr}';
    }
    // 大于1小时
    if (hours >= 1) {
      return '$hours ${Strings.hour.tr}';
    }
    // 小于1小时，显示 mm:ss
    return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
  }
}
