import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/config.dart';
import 'package:comunic/ui/dialogs/record_audio_dialog.dart';
import 'package:comunic/ui/routes/image_editor_route.dart';
import 'package:comunic/utils/files_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';

/// Pick file dialog
///
/// @author Pierre Hubert

enum _FileChoices {
  PICK_IMAGE,
  TAKE_PICTURE,
  PICK_VIDEO,
  TAKE_VIDEO,
  RECORD_AUDIO,
  PICK_OTHER_FILE,
}

typedef _CanEnable = bool Function(List<String>);
typedef _OnOptionSelected = void Function(_FileChoices);

class _PickFileOption {
  final _FileChoices value;
  final String label;
  final IconData icon;
  final _CanEnable canEnable;

  const _PickFileOption({
    required this.value,
    required this.label,
    required this.icon,
    required this.canEnable,
  });
}

List<_PickFileOption> get _optionsList => [
      // Image
      _PickFileOption(
          value: _FileChoices.PICK_IMAGE,
          label: tr("Choose an image")!,
          icon: Icons.image,
          canEnable: (l) => l.any(isImage)),
      _PickFileOption(
          value: _FileChoices.TAKE_PICTURE,
          label: tr("Take a picture")!,
          icon: Icons.camera_alt,
          canEnable: (l) => l.any(isImage)),

      // Video
      _PickFileOption(
          value: _FileChoices.PICK_VIDEO,
          label: tr("Choose a video")!,
          icon: Icons.video_library,
          canEnable: (l) => l.any(isVideo)),
      _PickFileOption(
          value: _FileChoices.TAKE_VIDEO,
          label: tr("Take a video")!,
          icon: Icons.videocam,
          canEnable: (l) => l.any(isVideo)),

      // Audio
      _PickFileOption(
          value: _FileChoices.RECORD_AUDIO,
          label: tr("Record audio")!,
          icon: Icons.mic,
          canEnable: (l) => l.any(isAudio)),

      // Other
      _PickFileOption(
          value: _FileChoices.PICK_OTHER_FILE,
          label: tr("Browse files")!,
          icon: Icons.folder_open,
          canEnable: (l) =>
              l.any((el) => !isImage(el) && !isVideo(el) && !isAudio(el))),
    ];

Future<BytesFile?> showPickFileDialog({
  required BuildContext context,
  int? maxFileSize,
  required List<String> allowedMimeTypes,
  int? imageMaxWidth,
  int? imageMaxHeight,
  CropAspectRatio? aspectRatio,
}) async {
  // Get the list of allowed extension
  final allowedExtensions = <String>[];
  for (var mime in allowedExtensions) {
    final ext = extensionFromMime(mime);
    if (ext != mime) allowedExtensions.add(ext);
  }

  // Display bottom sheet
  final choice = await showModalBottomSheet(
      context: context,
      builder: (c) => BottomSheet(
            onClosing: () {},
            builder: (c) => _BottomSheetPickOption(
              options: _optionsList
                  .where((element) => element.canEnable(allowedMimeTypes))
                  .toList(),
              onOptionSelected: (v) => Navigator.pop(c, v),
            ),
          ));

  if (choice == null) return null;

  BytesFile? file;
  switch (choice) {
    // Pick an image
    case _FileChoices.PICK_IMAGE:
    case _FileChoices.TAKE_PICTURE:
      final image = await ImagePicker().pickImage(
        source: choice == _FileChoices.PICK_IMAGE
            ? ImageSource.gallery
            : ImageSource.camera,
        maxWidth: imageMaxWidth!.toDouble(),
        maxHeight: imageMaxHeight!.toDouble(),
      );

      if (image == null) return null;

      file = BytesFile(image.path.split("/").last, await image.readAsBytes());

      file = await showImageCropper(context, file, aspectRatio: aspectRatio);

      break;

    // Pick an video
    case _FileChoices.PICK_VIDEO:
    case _FileChoices.TAKE_VIDEO:
      final image = await ImagePicker().pickVideo(
        source: choice == _FileChoices.PICK_VIDEO
            ? ImageSource.gallery
            : ImageSource.camera,
      );

      if (image == null) return null;

      file = BytesFile(image.path.split("/").last, await image.readAsBytes());

      break;

    // Record audio file
    case _FileChoices.RECORD_AUDIO:
      final bytes = await showRecordAudioDialog(context);
      if (bytes == null) return null;
      file = BytesFile("record.mp3", bytes);
      break;

    // Pick other files
    case _FileChoices.PICK_OTHER_FILE:
      final pickedFile = await FilePicker.platform.pickFiles(
        type: FileType.any,
        allowedExtensions: allowedExtensions,
        allowMultiple: false,
        withData: true,
      );

      if (pickedFile == null || pickedFile.files.length == 0) return null;

      file = BytesFile(pickedFile.files[0].name, pickedFile.files[0].bytes);
      break;
  }

  if (file == null) return null;

  // Check file size
  if (maxFileSize != null && file.bytes!.length > maxFileSize) {
    showSimpleSnack(
        context,
        tr("This file could not be sent: it is too big! (Max allowed size: %1%)",
            args: {"1": filesize(file.bytes!.length)})!);
    return null;
  }

  return file;
}

class _BottomSheetPickOption extends StatelessWidget {
  final List<_PickFileOption> options;
  final _OnOptionSelected onOptionSelected;

  const _BottomSheetPickOption(
      {Key? key, required this.options, required this.onOptionSelected})
      : super(key: key);

  @override
  Widget build(BuildContext context) => Container(
        color: config().splashBackgroundColor,
        height: 255,
        child: Center(
          child: ConstrainedBox(
            constraints: BoxConstraints(maxWidth: 400),
            child: ListView.builder(
              itemCount: options.length,
              itemBuilder: (c, i) => ListTile(
                leading: Icon(options[i].icon, color: Colors.white),
                title: Text(options[i].label,
                    style: TextStyle(color: Colors.white)),
                onTap: () => onOptionSelected(options[i].value),
              ),
            ),
          ),
        ),
      );
}