import 'dart:async';

import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:kasy_kit/core/data/entities/upload_result.dart';
import 'package:path_provider/path_provider.dart';
import 'package:universal_io/io.dart';

final storageApiProvider = Provider<StorageApi>((ref) => FirebaseStorageApi());

abstract class StorageApi {
  /// upload a file to firebase storage
  /// and return a stream of the upload progress
  Stream<UploadResult> uploadData(
    Uint8List data,
    String folder,
    String filename, {
    String? mimeType, // ex 'image/jpg'
    bool isPublic = true,
  });

  /// request to delete a file from path
  Future<void> deleteFile(String? path);
}

class FirebaseStorageApi implements StorageApi {
  FirebaseStorageApi();

  @override
  Stream<UploadResult> uploadData(
    Uint8List data,
    String folder,
    String filename, {
    String? mimeType, // ex 'image/jpg'
    bool isPublic = true,
  }) async* {
    final ref = FirebaseStorage.instance
        .ref() //
        .child(folder) //
        .child('/$filename');
    final metadata = SettableMetadata(contentType: mimeType);
    final uploadTask = ref.putData(data, metadata);
    await for (final event in uploadTask.snapshotEvents) {
      final totalBytes = event.totalBytes;
      final progress = totalBytes <= 0
          ? 0.0
          : event.bytesTransferred / totalBytes;
      yield UploadResultProgress(_normalizeProgress(progress));
      if (event.state == TaskState.success) {
        break;
      }
    }
    // Await the task to ensure the file is fully committed before fetching the URL.
    final taskSnapshot = await uploadTask;
    final imagePath = taskSnapshot.ref.fullPath;
    if (!isPublic) {
      yield UploadResultCompleted(imagePath: imagePath, imagePublicUrl: '');
      return;
    }
    final publicUrl = await taskSnapshot.ref.getDownloadURL();
    yield UploadResultCompleted(
      imagePath: imagePath,
      imagePublicUrl: publicUrl,
    );
  }

  double _normalizeProgress(double progress) {
    if (!progress.isFinite || progress <= 0) return 0.0;
    if (progress >= 1) return 1.0;
    return progress;
  }

  /// generate a public url for a file
  /// without this function, the url is only accessible by the app
  Future<UploadResultCompleted> generatePublicUrl(String path) {
    return FirebaseStorage.instance
        .ref(path)
        .getDownloadURL()
        .then(
          (value) =>
              UploadResultCompleted(imagePath: path, imagePublicUrl: value),
        );
  }

  Stream<TaskSnapshot> downloadFile(String path) async* {
    if (kIsWeb) {
      throw UnsupportedError('File download to disk is not supported on web.');
    }
    // if (await Permission.storage.request().isDenied) {
    //   throw "You must grant write permission to download files.";
    // }
    final fileRef = FirebaseStorage.instance.ref().child(path);
    final fileName = fileRef.name;
    Directory directory;
    if (Platform.isAndroid) {
      directory = Directory("/storage/emulated/0/Download");
    } else {
      directory = await getApplicationDocumentsDirectory();
    }
    final File destinationFile = File('${directory.path}/$fileName');
    if (destinationFile.existsSync()) {
      destinationFile.deleteSync();
    }
    destinationFile.createSync();
    final task = fileRef.writeToFile(destinationFile);
    yield* task.asStream();
  }

  @override
  Future<void> deleteFile(String? path) async {
    final ref = FirebaseStorage.instance.ref(path);
    await ref.delete();
  }
}
