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

/// Android WebView 的渲染模式。
enum AndroidRenderMode {
  /// 使用纹理层渲染模式。
  texture,

  /// 使用混合合成渲染模式。
  hybrid
  ;

  /// 当前模式是否使用混合合成。
  bool get usesHybridComposition => this == AndroidRenderMode.hybrid;

  /// 返回当前渲染模式对应的稳定日志标识。
  String get logName => switch (this) {
    AndroidRenderMode.texture => 'texture-layer',
    AndroidRenderMode.hybrid => 'hybrid-composition',
  };

  /// 返回当前渲染模式面向用户的显示名称。
  String get displayName => switch (this) {
    AndroidRenderMode.texture => '标准模式',
    AndroidRenderMode.hybrid => '兼容模式',
  };
}

/// Android 设备及系统 WebView 的运行信息。
class AndroidWebViewInfo {
  /// 创建 Android 设备与 WebView 元数据快照。
  AndroidWebViewInfo({
    required this.sdkInt,
    required this.manufacturer,
    required this.brand,
    required this.model,
    this.webViewDataDirectorySuffix,
    this.webViewPackageName,
    this.webViewVersionName,
    this.webViewLongVersionCode,
  }) : webViewMajorVersion = _parseWebViewMajorVersion(webViewVersionName);

  /// 根据平台通道返回的原始数据创建实例。
  factory AndroidWebViewInfo.fromMap(Map<Object?, Object?> raw) {
    return AndroidWebViewInfo(
      sdkInt: _readInt(raw['sdkInt']),
      manufacturer: _readString(raw['manufacturer']),
      brand: _readString(raw['brand']),
      model: _readString(raw['model']),
      webViewDataDirectorySuffix: _readNullableString(
        raw['webViewDataDirectorySuffix'],
      ),
      webViewPackageName: _readNullableString(raw['webViewPackageName']),
      webViewVersionName: _readNullableString(raw['webViewVersionName']),
      webViewLongVersionCode: _readNullableInt(raw['webViewLongVersionCode']),
    );
  }

  /// 当前设备的 Android SDK 版本。
  final int sdkInt;

  /// 设备制造商。
  final String manufacturer;

  /// 设备品牌。
  final String brand;

  /// 设备型号。
  final String model;

  /// 当前 WebView 数据目录后缀；有值时返回。
  final String? webViewDataDirectorySuffix;

  /// 已安装 WebView 的包名；有值时返回。
  final String? webViewPackageName;

  /// 已安装 WebView 的版本名；有值时返回。
  final String? webViewVersionName;

  /// 已安装 WebView 的长版本号；有值时返回。
  final int? webViewLongVersionCode;

  /// 从 [webViewVersionName] 中解析出的 WebView 主版本号。
  final int? webViewMajorVersion;

  /// 当前 WebView 是否应按旧版本处理。
  bool get isLegacyWebView =>
      webViewMajorVersion != null &&
      webViewMajorVersion! <= _legacyWebViewMajorVersionThreshold;

  /// 当前设备是否命中已知问题机型 `F136A`。
  bool get isF136A => model.toUpperCase() == 'F136A';

  /// 返回用于诊断的精简日志摘要。
  String get summary {
    final deviceSummary = [
      manufacturer,
      model,
    ].where((part) => part.isNotEmpty).join(' ');
    final parts = <String>[
      'sdk=$sdkInt',
      if (manufacturer.isNotEmpty || model.isNotEmpty) 'device=$deviceSummary',
      if (webViewPackageName case final String packageName
          when packageName.isNotEmpty)
        'webViewPackage=$packageName',
      if (webViewVersionName case final String versionName
          when versionName.isNotEmpty)
        'webViewVersion=$versionName',
      if (webViewDataDirectorySuffix case final String suffix
          when suffix.isNotEmpty)
        'webViewSuffix=$suffix',
    ];
    return parts.join(', ');
  }

  static int _readInt(Object? value, {int fallback = 0}) {
    if (value is int) {
      return value;
    }
    return int.tryParse((value ?? '').toString()) ?? fallback;
  }

  static int? _readNullableInt(Object? value) {
    if (value == null) {
      return null;
    }
    if (value is int) {
      return value;
    }
    return int.tryParse(value.toString());
  }

  static String _readString(Object? value) {
    return (value ?? '').toString().trim();
  }

  static String? _readNullableString(Object? value) {
    final normalized = _readString(value);
    return normalized.isEmpty ? null : normalized;
  }
}

/// Android WebView 的兼容策略。
class AndroidCompatibilityPlan {
  /// 创建 WebView 兼容策略。
  const AndroidCompatibilityPlan({
    required this.renderModes,
    required this.useWideViewPort,
    required this.suggestWebViewUpdate,
    required this.prefersAggressiveRecovery,
  });

  /// 创建设备信息未知时使用的默认兜底策略。
  factory AndroidCompatibilityPlan.fallback() {
    return const AndroidCompatibilityPlan(
      renderModes: kDebugMode
          ? <AndroidRenderMode>[
              AndroidRenderMode.texture,
              AndroidRenderMode.hybrid,
            ]
          : <AndroidRenderMode>[
              AndroidRenderMode.hybrid,
              AndroidRenderMode.texture,
            ],
      useWideViewPort: true,
      suggestWebViewUpdate: false,
      prefersAggressiveRecovery: true,
    );
  }

  /// 根据当前 Android 设备与 WebView 信息构建策略。
  factory AndroidCompatibilityPlan.fromInfo(AndroidWebViewInfo? info) {
    if (info == null) {
      return AndroidCompatibilityPlan.fallback();
    }

    final legacyAndroid = info.sdkInt <= 28;
    final legacyWebView = info.isLegacyWebView;
    final preferHybridFirst = info.isF136A || legacyAndroid || legacyWebView;

    return AndroidCompatibilityPlan(
      renderModes: preferHybridFirst
          ? const <AndroidRenderMode>[
              AndroidRenderMode.hybrid,
              AndroidRenderMode.texture,
            ]
          : const <AndroidRenderMode>[
              AndroidRenderMode.texture,
              AndroidRenderMode.hybrid,
            ],
      useWideViewPort: true,
      suggestWebViewUpdate: info.isF136A || legacyWebView || info.sdkInt <= 26,
      prefersAggressiveRecovery: info.isF136A || legacyAndroid || legacyWebView,
    );
  }

  /// 按优先级排序的候选渲染模式。
  final List<AndroidRenderMode> renderModes;

  /// 是否启用宽视口支持。
  final bool useWideViewPort;

  /// 界面是否需要提示用户更新系统 WebView。
  final bool suggestWebViewUpdate;

  /// 恢复流程是否使用更激进的策略。
  final bool prefersAggressiveRecovery;

  /// 返回精简的诊断描述。
  String describe() {
    return [
      'modes=${renderModes.map((mode) => mode.logName).join(' -> ')}',
      'wideViewport=$useWideViewPort',
      'aggressiveRecovery=$prefersAggressiveRecovery',
    ].join(', ');
  }
}

const int _legacyWebViewMajorVersionThreshold = 110;

int? _parseWebViewMajorVersion(String? versionName) {
  if (versionName == null || versionName.isEmpty) {
    return null;
  }
  final candidate = versionName.split('.').first.trim();
  return int.tryParse(candidate);
}
