import 'dart:io';
import 'dart:math';

import 'package:app_settings/app_settings.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart' as getx;
import 'package:path/path.dart' as path;
import 'package:o2oa_all_platform/common/extension/index.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';

import '../../environment_config.dart';
import '../api/index.dart';
import '../models/index.dart';
import '../services/index.dart';
import '../widgets/index.dart';
import 'flutter_mehod_channel_util.dart';
import 'loading.dart';
import 'log_util.dart';
import 'o2_api_manager.dart';
import 'o2_file_path_util.dart';
import 'o2_http_client.dart';

typedef O2AndroidAppDownloadProgressListener = void Function(double progress);

/// android app 更新工具类
class O2AndroidAppUpdateManager {
  factory O2AndroidAppUpdateManager() => _instance;

  static final O2AndroidAppUpdateManager _instance =
      O2AndroidAppUpdateManager._internal();

  O2AndroidAppUpdateManager._internal();

  // app 检查更新的地址
  final appUpdateJson = 'https://app.o2oa.net/download/app.json';
  final appDemoUpdateJson =
      'https://app.o2oa.net/download/app-sample-edition.json';
  // 是否正在运行
  bool isChecking = false;

  O2AndroidAppDownloadProgressListener? _downloadListener;

  /// 进度监听
  void setDownloadListener(O2AndroidAppDownloadProgressListener listener) {
    _downloadListener = listener;
  }

  void clearDownloadListener() {
    _downloadListener = null;
  }

  /// 检查更新
  checkUpdate({needToast = false}) async {
    if (isChecking) {
      return;
    }
    if (!Platform.isAndroid) {
      OLogger.d('不是 Android');
      return;
    }
    // 判断今天是否已经检查过
    final today = DateTime.now().ymd();
    final date = SharedPreferenceService.to
        .getString(SharedPreferenceService.appUpdateTodayKey);
    if (date.isNotEmpty && today == date && needToast == false) {
      OLogger.e('今日已经检查过！$date $today');
      return;
    }
    isChecking = true;
    // 直连模式 检查自助打包结果
    if (EnvironmentConfig.isDirectConnectMode()) {
      if (EnvironmentConfig.o2AppIsDemo) {
        // 演示版本
        _officialApp(needToast: needToast);
      } else {
        _innerApp(needToast: needToast);
      }
    } else {
      _officialApp(needToast: needToast);
    }
  }

  /// 自助打包 app 更新
  void _innerApp({needToast = false}) async {
    Map<String, dynamic>? config = await O2ApiManager.instance.getWebConfigJson();
    String? appPackBaseUrl;
    if (config != null && config['appUpdateUrl'] != null) {
      appPackBaseUrl = config['appUpdateUrl'];
    }
    OLogger.d('开始检查自助打包 包更新！配置文件地址：$appPackBaseUrl');
    final app =
        await AppPackingClientAssembleControlService.to.androidPackLastAPk(outBaseUrl: appPackBaseUrl);
    if (app == null) {
      OLogger.e('请求在线 app 版本信息 失败 ');
      isChecking = false;
      if (needToast) {
        Loading.toast('request_error'.tr);
      }
      return;
    }
    final buildNo = app.appVersionNo ?? '';
    final versionName = app.appVersionName ?? '';
    OLogger.i('线上 app $buildNo $versionName');
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    String buildNumber = packageInfo.buildNumber;
    String version = packageInfo.version;
    OLogger.i('当前版本号，$buildNumber $version');
    if (buildNo.compareTo(buildNumber) > 0) {
      OLogger.d('有新版本的 app！');
      String downloadUrl = AppPackingClientAssembleControlService.to
          .apkDownloadUrl(app.id ?? '', outBaseUrl: appPackBaseUrl);
      if (app.apkDownloadUrl != null && app.apkDownloadUrl!.isNotEmpty) {
        downloadUrl = app.apkDownloadUrl!;
        OLogger.i('有外部下载地址: $downloadUrl');
      }
      final fileName = app.name ?? '';
      _openUpdateDialog(versionName, '', downloadUrl, fileName);
    } else {
      isChecking = false;
      if (needToast) {
        Loading.toast('common_app_update_no_new_version'.tr);
      }
    }
  }

  /// 官网 app 更新
  void _officialApp({needToast = false}) async {
    OLogger.d('开始检查O2OA APP更新！');
    final result = await _loadAppJsonData();
    if (result == null) {
      OLogger.e('请求在线 app 版本信息 失败 ');
      isChecking = false;
      if (needToast) {
        Loading.toast('request_error'.tr);
      }
      return;
    }
    final buildNo = result.android?.buildNo ?? '';
    if (buildNo.isEmpty) {
      OLogger.e('没有 buildNo ');
      isChecking = false;
      if (needToast) {
        Loading.toast('common_app_update_no_new_version'.tr);
      }
      return;
    }
    OLogger.i(
        '线上 app ${result.android?.buildNo ?? '无'} ${result.android?.versionName ?? '无'}');
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    String buildNumber = packageInfo.buildNumber;
    String version = packageInfo.version;
    OLogger.i('当前版本号，$buildNumber $version');

    if (buildNo.compareTo(buildNumber) > 0) {
      OLogger.d('有新版本的 app！');
      final versionName = result.android?.versionName ?? '';
      final content = result.android?.content ?? '';
      final downloadUrl = result.android?.downloadUrl ?? '';
      final fileName = downloadUrl.fileName();
      _openUpdateDialog(versionName, content, downloadUrl, fileName);
    } else {
      isChecking = false;
      if (needToast) {
        Loading.toast('common_app_update_no_new_version'.tr);
      }
    }
  }

  /// 弹窗提示 更新 app
  void _openUpdateDialog(
      String versionName, String content, String downloadUrl, String fileName) {
    if (versionName.isEmpty || downloadUrl.isEmpty) {
      OLogger.e('$versionName $downloadUrl 参数为空！');
      isChecking = false;
      return;
    }
    final context = getx.Get.context;
    if (context == null) {
      OLogger.e('没有 context  无法弹窗！');
      isChecking = false;
      return;
    }
    showDialog(
        context: context,
        barrierDismissible: false,
        builder: (context) {
          return AlertDialog(
            title: Text('alert'.tr),
            content: Text('common_app_update_content_begin'
                .trArgs([versionName, content])),
            actions: [
              TextButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                    isChecking = false;
                  },
                  child: Text('cancel'.tr)),
              TextButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                    _later();
                    isChecking = false;
                  },
                  child: Text('common_app_update_not_today_button'.tr)),
              TextButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                    _launchDownloadApk(downloadUrl, fileName);
                  },
                  child: Text('common_app_update_positive_name'.tr))
            ],
          );
        });
  }

  /// 今日不再提醒
  _later() {
    // 写入当前日期 表示今天已经检查过了
    final today = DateTime.now().ymd();
    SharedPreferenceService.to
        .putString(SharedPreferenceService.appUpdateTodayKey, today);
  }

  /// 检查安装apk权限
  Future<bool> _checkInstallPackagesPermission() async {
    var status = await Permission.requestInstallPackages.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.requestInstallPackages.request();
      if (status == PermissionStatus.granted) {
        return true;
      }
    }
    return false;
  }

  Future<void> _launchDownloadApk(String downloadUrl, String fileName) async {
    final isInstalled = await _checkInstallPackagesPermission();
    if (isInstalled) {
      OLogger.d(' 开始下载。。。。');
      _startDownloadAPK(downloadUrl, fileName);
    } else {
      OLogger.e('没有权限。。。');
      _goSettings();
      isChecking = false;
    }
  }

  /// 开始下载 apk 文件
  _startDownloadAPK(String downloadUrl, String fileName) async {
    if (fileName.isEmpty) {
      OLogger.e('文件名称为空！');
      isChecking = false;
      return;
    }
    OLogger.d('下载 apk 地址: $downloadUrl');
    // 本地存储路径
    final tempFolder = await O2FilePathUtil.getTempFolderWithUrl(downloadUrl);
    if (tempFolder == null || tempFolder.isEmpty) {
      OLogger.e('存储目录为空！');
      isChecking = false;
      return;
    }
    final filePath =
        path.join(tempFolder, fileName); // '$tempFolder/$fileName';
    File file = File(filePath);
    if (file.existsSync()) {
      //  删除老文件
      file.delete();
    }
    // 下载进度
    showDownloadingDialog();
    Response response = await O2HttpClient.instance
        .downloadFile(downloadUrl, filePath, onReceiveProgress: (count, total) {
      if (_downloadListener != null) {
        _downloadListener!(count / total);
      }
    });
    clearDownloadListener();
    if (response.statusCode != 200) {
      Loading.showError('im_chat_error_download_file'.tr);
      if (file.existsSync()) {
        // 下载失败 删除文件
        file.delete();
      }
      isChecking = false;
      return;
    }
    // 安装 apk
    OLogger.i('开始安装 apk， $filePath');
    O2FlutterMethodChannelUtils().installApk(filePath);
    isChecking = false;
  }

  /// 去设置权限
  void _goSettings() {
    final context = getx.Get.context;
    if (context == null) {
      OLogger.e('没有 context  无法弹窗！');
      return;
    }
    O2UI.showConfirm(context, 'common_app_update_install_permission_confirm'.tr,
        okText: 'common_app_update_install_go_setting'.tr, okPressed: () {
      // openAppSettings(); // 打开设置
      AppSettings.openSecuritySettings();
    });
  }

  ///  请求在线 app 版本信息
  Future<O2AppUpdateInfo?> _loadAppJsonData() async {
    try {
      final r = Random();
      final s = r.nextInt(1000); // 添加一个随机数 防止缓存
      String url = '$appUpdateJson?t=$s';
      if (EnvironmentConfig.o2AppIsDemo) {
        url = '$appDemoUpdateJson?t=$s';
      }
      Response<dynamic> response = await O2HttpClient.instance.getOuter(url);
      return O2AppUpdateInfo.fromJson(response.data);
    } catch (err, stackTrace) {
      OLogger.e('更新 app 请求', err, stackTrace);
    }
    return null;
  }

  void showDownloadingDialog() {
    final context = getx.Get.context;
    if (context == null) {
      OLogger.e('没有 context  无法弹窗！');
      return;
    }
    showDialog(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return const AppUpdateLoading();
        });
  }
}
