// Author: fengshengxiong
// Date: 2024/5/10
// Description: 下载管理

import 'package:background_downloader/background_downloader.dart';
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:tone_snap/components/base_easyloading.dart';
import 'package:tone_snap/data/api/music_api.dart';
import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/models/player_model.dart';
import 'package:tone_snap/data/storage/offline_box.dart';
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
import 'package:tone_snap/utils/date_util.dart';
import 'package:tone_snap/utils/local_path_util.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';

class DownloadManager {
  static final DownloadManager _instance = DownloadManager._getInstance();

  factory DownloadManager() => _instance;

  static MemoryTaskQueue? tq;

  List<Rx<MusicModel>> downloadList = [];

  DownloadManager._getInstance() {
    if (tq == null) {
      tq ??= MemoryTaskQueue();
      tq!.maxConcurrent = 3; // no more than 5 tasks active at any one time
      tq!.maxConcurrentByHost = 3; // no more than two tasks talking to the same host at the same time
      tq!.maxConcurrentByGroup = 3; // no more than three tasks from the same group active at the same time
      FileDownloader().addTaskQueue(tq!); // 'connects' the TaskQueue to the FileDownloader
      FileDownloader().updates.listen((update) async { // listen to updates as per usual
        Rx<MusicModel>? musicModel = downloadList.firstWhereOrNull((e) => e.value.videoId == update.task.taskId);
        if (musicModel == null) return;

        if (update.runtimeType == TaskStatusUpdate) {
          TaskStatus taskStatus = (update as TaskStatusUpdate).status;
          LogUtil.d('${update.task.filename},任务状态: $taskStatus');
          musicModel.update((fn) => fn?.taskStatus = taskStatus);
          switch (taskStatus) {
            case TaskStatus.enqueued:
              break;
            case TaskStatus.running:
              break;
            case TaskStatus.complete:
              LogUtil.d('音乐下载路径：${await update.task.filePath()}');
              musicModel.value.localPath = await update.task.filePath();
              OfflineBox().add(musicModel.value.copyWith());
              downloadList.remove(musicModel);
              BaseEasyLoading.toast('Download completed');
              if (Get.isRegistered<PersonalMusicLibraryController>()) {
                PersonalMusicLibraryController.to.refreshOffline();
              }
              if (Get.isRegistered<OfflineController>()) {
                OfflineController.to.getOfflineList();
              }
              break;
            case TaskStatus.notFound:
              BaseEasyLoading.toast('Download failed');
              downloadList.remove(musicModel);
              break;
            case TaskStatus.failed:
              BaseEasyLoading.toast('Download failed');
              downloadList.remove(musicModel);
              break;
            case TaskStatus.canceled:
              BaseEasyLoading.toast('Download cancelled');
              downloadList.remove(musicModel);
              break;
            case TaskStatus.waitingToRetry:
              break;
            case TaskStatus.paused:
              break;
          }
        }
        if (update.runtimeType == TaskProgressUpdate) {
          LogUtil.d('${update.task.filename},下载进度: $update');
          musicModel.update((fn) => fn?.progress = (update as TaskProgressUpdate).progress);
        }
      });
    }
  }

  /// 下载文件
  void downloadMusic(Rx<MusicModel>? musicModel) {
    if (musicModel == null) return;
    musicModel.update((fn) {
      fn?.taskStatus = TaskStatus.enqueued;
      fn?.cancelToken = CancelToken();
    });
    _getMusicUrl(musicModel, (url, mimeType) async {
      if (ObjUtil.isEmpty(url)) {
        BaseEasyLoading.toast('Resource acquisition failed');
        musicModel.update((fn) => fn?.taskStatus = TaskStatus.failed);
        return;
      }
      String extension = mimeType ?? 'mp4';
      if (ObjUtil.isNotEmpty(mimeType)) {
        // 从 mimeType 中提取主类型和子类型
        String type = mimeType!.split(';')[0].trim();
        // 获取文件扩展名
        extension = type.split('/')[1];
      }
      final filename = '${musicModel.value.title}_${DateUtil.getNowTimestamp()}.$extension';
      final task = DownloadTask(
        taskId: musicModel.value.videoId,
        url: url!,
        filename: filename,
        directory: LocalPathUtil.getMusicDownloadDir(),
        updates: Updates.statusAndProgress,
        requiresWiFi: false,
        retries: 0,
        allowPause: false,
        metaData: '',
      );
      tq?.add(task);
      downloadList.add(musicModel);
    });
  }

  Future<void> _getMusicUrl(Rx<MusicModel> musicModel, Function(String? url, String? mimeType) onTap) async {
    PlayerModel? playerModel = await MusicApi.player(
      videoId: musicModel.value.videoId,
      cancelToken: musicModel.value.cancelToken,
      fail: (baseError) {
        if (baseError.code == DioExceptionType.cancel.index) {
          musicModel.update((fn) {
            fn?.taskStatus = TaskStatus.canceled;
            fn?.cancelToken = null;
          });
        } else {
          musicModel.update((fn) {
            fn?.taskStatus = TaskStatus.failed;
            fn?.cancelToken = null;
          });
        }
      }
    );
    if (playerModel != null) {
      if (ObjUtil.isEmpty(musicModel.value.coverUrl)) {
        var thumbnails = playerModel.videoDetails?.thumbnail?.thumbnails;
        if (thumbnails != null && thumbnails.isNotEmpty) {
          musicModel.value.coverUrl = thumbnails[0].url;
        }
      }
      if (ObjUtil.isEmpty(musicModel.value.musicType)) {
        musicModel.value.musicType = playerModel.videoDetails?.musicVideoType;
      }
      var formats = playerModel.streamingData?.formats;
      if (formats != null && formats.isNotEmpty) {
        onTap(formats[0].url, formats[0].mimeType);
      }
    }
  }

  void cancelDownload(Rx<MusicModel>? musicModel) {
    if (musicModel == null || musicModel.value.videoId == null) return;
    musicModel.value.cancelToken?.cancel();
    FileDownloader().cancelTaskWithId(musicModel.value.videoId!);
  }
}
