import 'dart:convert';
import 'dart:typed_data';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:nut_player/nut_player.dart';
import 'package:nut_player_example/src/features/main_screen/domain/firebase/models/firebase_content.dart';
import 'package:nut_player_example/src/features/main_screen/domain/firebase/models/firebase_error.dart';
import 'package:nut_player_example/src/features/main_screen/domain/firebase/models/firebase_subtitles.dart';
import 'package:nut_player_example/src/features/main_screen/domain/firebase/models/firebase_statistics.dart';
import 'package:nut_player_example/src/features/main_screen/domain/firebase/parsing/response.dart';

class FirebaseProvider extends Provider {
  final String fileName;
  static const root = "PlayableContent/";

  FirebaseProvider(this.fileName);

  static Future<List<String>> getConfigNames() async {
    var storage = FirebaseStorage.instance.ref();
    var values = await storage.child(root).listAll();
    return values.items.map((e) => e.name).toList();
  }

  @override
  Future<PlayerContent> retrieveContent() async {
    var storage = FirebaseStorage.instance.ref();
    final file = storage.child("$root$fileName");

    try {
      var results = await file.getData(5 * 1024 * 1024);
      return _handleLoadedData(results);
    } catch (error) {
      var fbError = FirebaseGetDataError(error);
      throw Future.error(fbError);
    }
  }

  FirebaseContent _handleLoadedData(Uint8List? data) {
    if (data == null) { throw FirebaseEmptyDataError(); }

    var encoded = base64Encode(data);
    var decoded = utf8.fuse(base64).decode(encoded);

    var json = jsonDecode(decoded);
    var response = Response.fromJson(json);

    if (response.playbacks.isEmpty) { throw FirebaseNoPlaybacksError(); }
    var uri = Uri.tryParse(response.playbacks.first.streamUrl);
    if (uri != null) {
      final statistics = response.statistics.map((stat) =>
          FirebaseStatistics(
            stat.name,
            stat.urlTemplate,
            stat.start,
            stat.delay,
            stat.count,
            stat.method == "get" ? HTTPMethod.get : HTTPMethod.post,
            stat.body
          )
      );

      final subtitles = response.subtitles.map((sub) =>
          FirebaseSubtitles(
              sub.title,
              sub.type == "srt" ? SubtitleType.srt : SubtitleType.unknown,
              sub.url,
              sub.language
          )
      );

      final ContentType? contentType;
      final urlPath = uri.toString();
      if (urlPath.endsWith('.mp4')) {
        contentType = Mp4ContentType(urlPath: urlPath);
      } else if (urlPath.endsWith('.m3u8')) {
        final isLive = response.playbacks.first.isLive;
        contentType = HlsContentType(urlPath: urlPath, isLive: isLive);
      } else {
        contentType = null;
      }

      if (contentType != null) {
        return FirebaseContent(
            contentType,
            statistics.toList(),
            subtitles.toList()
        );
      } else {
        throw FirebaseUnknownFormatError();
      }
    } else {
      throw FirebaseIncorrectUrlError();
    }
  }
}