part of '../../core_app.dart';

/// 升级明细配置，对应升级接口中的 `upgrade` 字段。
class ShellUpgradeReleaseConfig {
  /// 创建一份升级明细配置。
  const ShellUpgradeReleaseConfig({
    this.versionName,
    this.version,
    this.isForce,
    this.remark,
    this.filePath,
    this.fileSize,
  });

  /// 从 JSON 映射解析升级明细配置。
  factory ShellUpgradeReleaseConfig.fromJson(Map<String, dynamic> json) {
    return ShellUpgradeReleaseConfig(
      versionName: _readShellUpgradeString(json['versionName']),
      version: _readShellUpgradeInt(json['version']),
      isForce: _readShellUpgradeInt(json['isForce']),
      remark: _readShellUpgradeString(json['remark']),
      filePath: _readShellUpgradeString(json['filePath']),
      fileSize: _readShellUpgradeInt(json['fileSize']),
    );
  }

  /// 升级版本名称。
  final String? versionName;

  /// 升级版本号。
  final int? version;

  /// 是否强制升级。
  final int? isForce;

  /// 升级说明。
  final String? remark;

  /// 安装包下载地址。
  final String? filePath;

  /// 安装包大小，单位为 KB。
  final int? fileSize;

  /// 兼容已经解码好的 Map，或 `data.config.upgrade` 这种 JSON 字符串。
  static ShellUpgradeReleaseConfig? fromDynamic(dynamic value) {
    if (value == null) {
      return null;
    }
    if (value is Map<String, dynamic>) {
      return ShellUpgradeReleaseConfig.fromJson(value);
    }
    if (value is Map) {
      return ShellUpgradeReleaseConfig.fromJson(
        Map<String, dynamic>.from(value),
      );
    }
    if (value is! String) {
      return null;
    }

    final trimmed = value.trim();
    if (trimmed.isEmpty) {
      return null;
    }

    try {
      final decoded = jsonDecode(trimmed);
      if (decoded is Map<String, dynamic>) {
        return ShellUpgradeReleaseConfig.fromJson(decoded);
      }
      if (decoded is Map) {
        return ShellUpgradeReleaseConfig.fromJson(
          Map<String, dynamic>.from(decoded),
        );
      }
    } on Object catch (error, stackTrace) {
      debugPrint('解析 WebShell 升级配置 upgrade 字段异常: $error\n$stackTrace');
    }
    return null;
  }
}

/// 远程升级配置模型，对应升级接口的顶层 JSON。
///
/// 当前仅解析新接口结构：
/// 1. 顶层包含 `success/code/msg/data`；
/// 2. 升级与启动配置都放在 `data.config` 这个 JSON 字符串中；
/// 3. `data.config` 解开后，内部再拆为 `upgrade` 和 `config` 两部分。
class ShellUpgradeConfig {
  /// 创建一份远程升级配置。
  const ShellUpgradeConfig({
    this.success,
    this.responseCode,
    this.msg,
    this.id,
    this.upgradeFileId,
    this.installFileId,
    this.isLatest,
    this.appId,
    this.releaseCode,
    this.releaseTime,
    this.releaseStatus,
    this.upgradeOssUrl,
    this.isAllowRollback,
    this.rawConfig,
    this.configDescribe,
    this.describe,
    this.describeType,
    this.environment,
    this.createdTime,
    this.installOssUrl,
    this.upgrade,
    this.config,
  });

  /// 从接口响应 JSON 中解析升级配置。
  ///
  /// 会同时拆出升级信息和运行时启动配置。
  factory ShellUpgradeConfig.fromJson(Map<String, dynamic> json) {
    final data = json['data'];
    final dataMap = data is Map<String, dynamic>
        ? data
        : data is Map
        ? Map<String, dynamic>.from(data)
        : const <String, dynamic>{};
    final rawConfig = _readShellUpgradeString(dataMap['config']);
    final parsedConfig = _readShellUpgradeEmbeddedConfig(rawConfig);
    final rawBootstrapConfig = parsedConfig?['config'];
    final bootstrapConfig = rawBootstrapConfig is Map<String, dynamic>
        ? ShellBootstrapConfig.fromJson(rawBootstrapConfig)
        : rawBootstrapConfig is Map
        ? ShellBootstrapConfig.fromJson(
            Map<String, dynamic>.from(rawBootstrapConfig),
          )
        : null;

    return ShellUpgradeConfig(
      success: _readShellUpgradeBool(json['success']),
      responseCode: _readShellUpgradeString(json['code']),
      msg: _readShellUpgradeString(json['msg']),
      id: _readShellUpgradeInt(dataMap['id']),
      upgradeFileId: _readShellUpgradeInt(dataMap['upgradeFileId']),
      installFileId: _readShellUpgradeInt(dataMap['installFileId']),
      isLatest: _readShellUpgradeBool(dataMap['isLatest']),
      appId: _readShellUpgradeInt(dataMap['appId']),
      releaseCode: _readShellUpgradeString(dataMap['code']),
      releaseTime: _readShellUpgradeString(dataMap['releaseTime']),
      releaseStatus: _readShellUpgradeBool(dataMap['releaseStatus']),
      upgradeOssUrl: _readShellUpgradeString(dataMap['upgradeOssUrl']),
      installOssUrl: _readShellUpgradeString(dataMap['installOssUrl']),
      isAllowRollback: _readShellUpgradeBool(dataMap['isAllowRollback']),
      rawConfig: rawConfig,
      upgrade: ShellUpgradeReleaseConfig.fromDynamic(parsedConfig?['upgrade']),
      config: bootstrapConfig,
      configDescribe: _readShellUpgradeString(dataMap['configDescribe']),
      describe: _readShellUpgradeString(dataMap['describe']),
      describeType: _readShellUpgradeInt(dataMap['describeType']),
      environment: _readShellUpgradeInt(dataMap['environment']),
      createdTime: _readShellUpgradeString(dataMap['createdTime']),
    );
  }

  /// 接口返回的 success 标记。
  final bool? success;

  /// 接口返回码。
  final String? responseCode;

  /// 接口返回消息。
  final String? msg;

  /// 发布记录 ID。
  final int? id;

  /// 升级包文件 ID。
  final int? upgradeFileId;

  /// 安装包文件 ID。
  final int? installFileId;

  /// 服务端标记的是否已是最新版本。
  final bool? isLatest;

  /// 应用 ID。
  final int? appId;

  /// 发布编号。
  final String? releaseCode;

  /// 发布时间。
  final String? releaseTime;

  /// 发布状态。
  final bool? releaseStatus;

  /// 升级资源地址。
  final String? upgradeOssUrl;

  /// 是否允许回滚。
  final bool? isAllowRollback;

  /// 原始 `data.config` 字符串。
  final String? rawConfig;

  /// 服务端下发的配置描述。
  final String? configDescribe;

  /// 描述类型。
  final int? describeType;

  /// 所属环境标识。
  final int? environment;

  /// 创建时间。
  final String? createdTime;

  /// 结构化升级信息。
  final ShellUpgradeReleaseConfig? upgrade;

  /// 结构化运行时启动配置。
  final ShellBootstrapConfig? config;

  /// 安装包 OSS 地址。
  final String? installOssUrl;

  /// 服务端直接下发的展示文案。
  final String? describe;

  /// 供升级弹窗展示的版本名称。
  String? get versionName => upgrade?.versionName;

  /// 供升级比较使用的版本号。
  int? get version => upgrade?.version;

  /// 供升级弹窗识别的强更标记。
  int? get isForce => upgrade?.isForce;

  /// 优先取升级明细说明，顶层描述仅作兜底文案。
  String? get remark => upgrade?.remark ?? describe;

  /// 优先取安装地址，否则回退到升级明细中的文件地址。
  String? get filePath =>
      _readShellUpgradeString(installOssUrl ?? upgrade?.filePath);

  /// 安装包大小，单位为 KB。
  int? get fileSize => upgrade?.fileSize;

  /// 是否至少解析出一份结构化升级或启动配置。
  bool get hasStructuredPayload => upgrade != null || config != null;

  /// 仅使用远端 `upgrade.version` 与本地 buildNumber 做整数比较。
  ///
  /// 返回 `true` 表示远端版本更高，需要触发升级提示；
  /// 如果远端未下发版本号，则直接视为无需升级。
  bool shouldOfferUpgrade({required int localVersion}) {
    final remoteVersion = upgrade?.version;
    if (remoteVersion == null) {
      return false;
    }
    return remoteVersion > localVersion;
  }
}

String? _readShellUpgradeString(dynamic value) {
  final normalized = value?.toString().trim();
  if (normalized == null || normalized.isEmpty) {
    return null;
  }
  return normalized;
}

int? _readShellUpgradeInt(dynamic value) {
  if (value is int) {
    return value;
  }
  if (value is num) {
    return value.toInt();
  }
  final normalized = value?.toString().trim();
  if (normalized == null || normalized.isEmpty) {
    return null;
  }
  return int.tryParse(normalized);
}

bool? _readShellUpgradeBool(dynamic value) {
  if (value is bool) {
    return value;
  }
  if (value is num) {
    return value != 0;
  }
  final normalized = value?.toString().trim().toLowerCase();
  if (normalized == null || normalized.isEmpty) {
    return null;
  }
  if (normalized == 'true' || normalized == '1') {
    return true;
  }
  if (normalized == 'false' || normalized == '0') {
    return false;
  }
  return null;
}

/// 解析 `data.config` 这个嵌套 JSON 字符串。
Map<String, dynamic>? _readShellUpgradeEmbeddedConfig(String? value) {
  if (value == null || value.isEmpty) {
    return null;
  }

  try {
    final decoded = jsonDecode(value);
    if (decoded is Map<String, dynamic>) {
      return decoded;
    }
    if (decoded is Map) {
      return Map<String, dynamic>.from(decoded);
    }
  } on Object catch (error, stackTrace) {
    debugPrint('解析 WebShell 升级配置 data.config 异常: $error\n$stackTrace');
  }
  return null;
}

/// 管理壳应用的升级逻辑，依赖 yx_app_upgrade_flutter。
class ShellUpgradeService {
  ShellUpgradeService._();

  /// 单例入口。
  static final ShellUpgradeService instance = ShellUpgradeService._();

  String? _configUrl;
  Future<int?> Function()? _localBuildNumberResolver;

  /// 注入升级配置地址。
  ///
  /// 这里只保存升级接口 URL，不会立即发起网络请求。
  void setupConfigUrl(String? configUrl) {
    _configUrl = configUrl?.trim();
  }

  /// 检查应用版本，若配置中有更新则会弹出升级弹窗。
  ///
  /// 这里仅负责“拉取升级配置 + 触发升级插件比较”。
  /// `initialUrl`、`preferredOrientations` 等运行时配置刷新，
  /// 由 `core_app.dart` 中显式的 reload 方法单独处理。
  Future<void> checkVersion(
    BuildContext context, {
    bool showNoUpdateToast = false,
  }) async {
    final configUrl = _configUrl;
    if (configUrl == null || configUrl.isEmpty) {
      if (showNoUpdateToast) {
        _showToastFromBridge(context, {'message': '未配置升级地址'});
      }
      return;
    }

    final remoteConfig = await _fetchConfig(configUrl);
    if (!context.mounted) {
      return;
    }
    if (remoteConfig == null) {
      if (showNoUpdateToast) {
        _showToastFromBridge(context, {'message': '未获取到版本配置'});
      }
      return;
    }

    await UpgradeAuxiliaryUtils.instance.initiateVersionCheck(
      // coverage:ignore-line
      context,
      showNoUpdateToast: showNoUpdateToast,
      future: _createVersionResolver(remoteConfig), // coverage:ignore-line
    );
  }

  Future<AppUpgradeVersion?> Function(int upType) _createVersionResolver(
    ShellUpgradeConfig remoteConfig,
  ) {
    return (int upType) async {
      // 先把远端配置转换成升级插件需要的数据模型；
      // 如果关键字段不完整，则直接中止本次升级检查。
      final upgradeVersion = _convertToAppUpgradeVersion(remoteConfig);
      if (upgradeVersion == null) {
        return null;
      }

      // 先用 WebShell 自己的版本比较逻辑做一次前置过滤，
      // 避免远端版本不高时仍然进入插件弹窗流程。
      // 若本地 buildNumber 读取失败，则回退给升级插件做内部比较。
      final localBuildNumber = await _resolveLocalBuildNumber();
      if (localBuildNumber == null) {
        debugPrint('获取 WebShell 本地版本号失败，回退到升级插件内部版本比较');
        return upgradeVersion;
      }
      if (!remoteConfig.shouldOfferUpgrade(localVersion: localBuildNumber)) {
        debugPrint(
          'WebShell 远端版本(${remoteConfig.version}) '
          '<= 本地版本($localBuildNumber)，跳过升级提示',
        );
        return null;
      }
      return upgradeVersion;
    };
  }

  /// 优先使用测试注入的 resolver；否则回退到升级插件读取本地 buildNumber。
  Future<int?> _resolveLocalBuildNumber() async {
    final resolver = _localBuildNumberResolver;
    if (resolver != null) {
      return resolver();
    }

    try {
      final appInfo = await AppUpgradePlugin().getAppInfo();
      return int.tryParse((appInfo['buildNumber'] ?? '').trim());
    } on Object catch (error, stackTrace) {
      debugPrint('获取 WebShell 本地版本号异常: $error\n$stackTrace');
      return null;
    }
  }

  /// 仅供测试注入本地版本号解析逻辑。
  @visibleForTesting
  Future<int?> Function()? get debugLocalBuildNumberResolver =>
      _localBuildNumberResolver;

  /// 仅供测试注入本地版本号解析逻辑。
  @visibleForTesting
  set debugLocalBuildNumberResolver(Future<int?> Function()? resolver) {
    _localBuildNumberResolver = resolver;
  }

  /// 从升级接口拉取原始 JSON，并交给统一解析器处理。
  Future<ShellUpgradeConfig?> _fetchConfig(String url) async {
    try {
      final uri = Uri.tryParse(url);
      if (uri == null) {
        return null;
      }
      debugPrint('WebShell 正在获取升级配置: $url');
      final response = await http.get(uri).timeout(const Duration(seconds: 10));
      if (response.statusCode != 200) {
        debugPrint('获取 WebShell 升级配置失败，状态码=${response.statusCode}');
        return null;
      }
      final content = utf8.decode(response.bodyBytes);
      return _parseConfigString(content);
    } on Object catch (error, stackTrace) {
      debugPrint(
        '获取 WebShell 升级配置失败(网络异常/超时): $error\n$stackTrace',
      );
      return null;
    }
  }

  /// 解析接口返回字符串。
  ///
  /// 只有当响应中至少成功解析出 `upgrade` 或 `config` 任一结构时，
  /// 才认为这是有效的升级配置。
  ShellUpgradeConfig? _parseConfigString(String content) {
    try {
      final dynamic jsonMap = jsonDecode(content);
      if (jsonMap is Map<String, dynamic>) {
        final config = ShellUpgradeConfig.fromJson(jsonMap);
        return config.hasStructuredPayload ? config : null;
      }
      if (jsonMap is Map) {
        final config = ShellUpgradeConfig.fromJson(
          Map<String, dynamic>.from(jsonMap),
        );
        return config.hasStructuredPayload ? config : null;
      }
    } on Object catch (error, stackTrace) {
      debugPrint('解析 WebShell 升级配置异常: $error\n$stackTrace');
    }
    return null;
  }

  /// 将远程 JSON 配置转换为升级弹窗所需的数据模型。
  ///
  /// 这里会把：
  /// - `upgrade.versionName` -> `versionName`
  /// - `upgrade.version` -> `versionBuildNumber`
  /// - `upgrade.isForce` -> `isForce`
  /// - `describe/upgrade.remark` -> `updateContent`
  /// - `installOssUrl/upgrade.filePath` -> 下载地址
  /// - `upgrade.fileSize`(KB) -> `apkSize`(Byte)
  AppUpgradeVersion? _convertToAppUpgradeVersion(ShellUpgradeConfig config) {
    if (config.version == null || config.versionName == null) {
      return null;
    }

    final apkSizeBytes = config.fileSize != null
        ? config.fileSize! * 1024
        : null;
    final filePath = (config.filePath != null && config.filePath!.isNotEmpty)
        ? config.filePath
        : null;

    return AppUpgradeVersion(
      versionName: config.versionName,
      versionBuildNumber: config.version,
      isForce: config.isForce == 1,
      updateContent: config.remark ?? '',
      downloadUrl: filePath,
      appStoreUrl: filePath,
      apkSize: apkSizeBytes,
      supportedMethods: [
        AppUpgradeMethod.browser,
        AppUpgradeMethod.inApp,
        AppUpgradeMethod.market,
      ],
    );
  }
}
