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

import 'package:file_picker/file_picker.dart';
import 'package:image/image.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';

import '../globals.dart';
import '../models/exec_error.dart';
import '../models/media_attachment_uploads/image_types_enum.dart';
import '../models/media_attachment_uploads/media_upload_attachment.dart';

class MediaUploadAttachmentHelper {
  static final _logger = Logger('$MediaUploadAttachmentHelper');

  static FutureResult<List<MediaUploadAttachment>, ExecError>
      getNewImagesFromCamera() async {
    final file = await ImagePicker().pickImage(source: ImageSource.camera);
    if (file != null) {
      return Result.ok([MediaUploadAttachment.newItem(file.path)]);
    }
    {
      return Result.ok([]);
    }
  }

  static FutureResult<List<MediaUploadAttachment>, ExecError>
      getImagesFromGallery() async {
    final files = <XFile>[];
    if (useImagePicker) {
      final picker = ImagePicker();
      final selectedFiles = await picker.pickMultiImage();
      files.addAll(selectedFiles);
    } else {
      await _desktopImagesFromDisk().match(
        onSuccess: (selectedFiles) => files.addAll(selectedFiles),
        onError: (error) => _logger.severe(error),
      );
    }

    return Result.ok(
        files.map((f) => MediaUploadAttachment.newItem(f.path)).toList());
  }

  static FutureResult<List<XFile>, dynamic> _desktopImagesFromDisk() async {
    final result = await FilePicker.platform.pickFiles(
      type: FileType.image,
      allowMultiple: true,
    );
    if (result == null) {
      return Result.ok([]);
    }
    final files = result.files.map((f) => XFile(f.path!)).toList();
    return Result.ok(files);
  }

  static Result<List<int>, dynamic> getUploadableImageBytes(String path,
      {int maxSizeBytes = 800000}) {
    return runCatching(() {
      final file = File(path);
      final fileBytes = file.readAsBytesSync();
      var size = file.statSync().size;

      if (size <= maxSizeBytes) {
        return Result.ok(fileBytes);
      }

      final imageType = ImageTypes.fromExtension(path);
      var scale = maxSizeBytes / size;
      late final Image original;
      switch (imageType) {
        case ImageTypes.gif:
          original = decodeGif(fileBytes)!;
          break;
        case ImageTypes.png:
          original = decodePng(fileBytes)!;
          break;
        case ImageTypes.jpg:
          original = decodeJpg(fileBytes)!;
          break;
      }
      late List<int> resizedBytes;
      while (size > maxSizeBytes) {
        final newWidth = (original.width * sqrt(scale)).toInt();
        final resized = copyResize(original, width: (newWidth).toInt());
        switch (imageType) {
          case ImageTypes.gif:
            resizedBytes = encodeGif(resized);
            break;
          case ImageTypes.png:
            resizedBytes = encodePng(resized);
            break;
          case ImageTypes.jpg:
            resizedBytes = encodeJpg(resized, quality: 80);
            break;
        }
        size = resizedBytes.length;
        scale /= 2;
      }

      return Result.ok(resizedBytes);
    });
  }
}
