// 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 extends GetxController {
  static DownloadManager get to => Get.put(DownloadManager(), permanent: true);
  final downloadStateId = 'download_state_id';

  MemoryTaskQueue? tq;

  List<MusicModel> downloadList = [];

  @override
  void onInit() {
    super.onInit();
    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
        MusicModel? musicModel = downloadList.firstWhereOrNull((e) => e.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.taskStatus = taskStatus;
          switch (taskStatus) {
            case TaskStatus.enqueued:
              break;
            case TaskStatus.running:
              break;
            case TaskStatus.complete:
              LogUtil.d('音乐下载路径：${await update.task.filePath()}');
              musicModel.localPath = await update.task.filePath();
              OfflineBox().add(musicModel);
              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.progress = (update as TaskProgressUpdate).progress;
        }
        updateDownloadState();
      });
    }
  }

  /// 下载文件
  void downloadMusic(MusicModel? m) {
    if (m == null) return;
    MusicModel musicModel = m.copyWith();
    musicModel.taskStatus = TaskStatus.enqueued;
    musicModel.cancelToken = CancelToken();
    downloadList.add(musicModel);
    updateDownloadState();

    _getMusicUrl(musicModel, (url, mimeType) async {
      if (ObjUtil.isNotEmpty(url)) {
        String extension = mimeType ?? 'mp4';
        if (ObjUtil.isNotEmpty(mimeType)) {
          // 从 mimeType 中提取主类型和子类型
          String type = mimeType!.split(';')[0].trim();
          // 获取文件扩展名
          extension = type.split('/')[1];
        }
        final filename = '${musicModel.title}_${DateUtil.getNowTimestamp()}.$extension';
        final task = DownloadTask(
          taskId: musicModel.videoId,
          url: url!,
          filename: filename,
          directory: LocalPathUtil.getMusicDownloadDir(),
          updates: Updates.statusAndProgress,
          requiresWiFi: false,
          retries: 0,
          allowPause: false,
          metaData: '',
        );
        tq?.add(task);
      }
    });
  }

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

  void cancelDownload(String? videoId) {
    if (ObjUtil.isEmpty(videoId)) return;
    final m = getMusicModel(videoId);
    if (m != null) {
      m.cancelToken?.cancel();
      if (m.videoId != null) {
        FileDownloader().cancelTaskWithId(m.videoId!);
      }
    }
  }

  void updateDownloadState() {
    DownloadManager.to.update([downloadStateId]);
  }

  MusicModel? getMusicModel(String? videoId) {
    if (ObjUtil.isEmpty(videoId)) return null;
    final m = downloadList.firstWhereOrNull((e) => e.videoId == videoId);
    return m;
  }
}
