/*
Copyright 2019-2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
 */

import Foundation
import UIKit
import AVFoundation
import PhotosUI

@objc protocol SingleImagePickerPresenterDelegate: AnyObject {
    func singleImagePickerPresenter(_ presenter: SingleImagePickerPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?)
    func singleImagePickerPresenterDidCancel(_ presenter: SingleImagePickerPresenter)
    func singleImagePickerPresenterDidRemoveImage(_ presenter: SingleImagePickerPresenter)
}

/// SingleImagePickerPresenter enables to present an image picker with single selection
@objcMembers
final class SingleImagePickerPresenter: NSObject {
    
    // MARK: - Constants
    
    private enum Constants {
        static let jpegCompressionQuality: CGFloat = 1.0
    }
    
    // MARK: - Properties
    
    // MARK: Private
    
    private let session: MXSession
    
    private weak var presentingViewController: UIViewController?
    private var cameraPresenter: CameraPresenter?
    private var mediaPickerPresenter: MediaPickerCoordinatorBridgePresenter?
    
    // MARK: Public
    
    weak var delegate: SingleImagePickerPresenterDelegate?
    var allowsRemoveImage = false
    
    // MARK: - Setup
    
    init(session: MXSession) {
        self.session = session
    }
    
    // MARK: - Public
    
    func present(from presentingViewController: UIViewController,
                 sourceView: UIView?,
                 sourceRect: CGRect,
                 animated: Bool) {
        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        
        
        let cameraAction = UIAlertAction(title: VectorL10n.imagePickerActionCamera, style: .default, handler: { _ in
            self.presentCamera(animated: animated)
        })
        
        let photoLibraryAction = UIAlertAction(title: VectorL10n.imagePickerActionLibrary, style: .default, handler: { _ in
            self.presentPhotoLibray(sourceView: sourceView, sourceRect: sourceRect, animated: animated)
        })
        
        let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel)
        
        alert.addAction(cameraAction)
        alert.addAction(photoLibraryAction)
        
        // BWI: remove avatars
        if BWIBuildSettings.shared.enableRemoveAvatarImage && allowsRemoveImage {
            let removePhotoAction = UIAlertAction(title: BWIL10n.imagePickerActionRemovePhoto, style: .destructive, handler: { _ in
                self.removeImage()
            })
            
            alert.addAction(removePhotoAction)
        }
        
        alert.addAction(cancelAction)
        
        if let popoverPresentationController = alert.popoverPresentationController {
            popoverPresentationController.sourceView = sourceView
            popoverPresentationController.sourceRect = sourceRect
        }
        
        presentingViewController.present(alert, animated: animated, completion: nil)
        self.presentingViewController = presentingViewController
    }
    
    func dismiss(animated: Bool, completion: (() -> Void)?) {
        if let cameraPresenter = self.cameraPresenter {
            cameraPresenter.dismiss(animated: animated, completion: completion)
        } else if let mediaPickerPresenter = self.mediaPickerPresenter {
            mediaPickerPresenter.dismiss(animated: animated, completion: completion)
        }
    }
    
    // MARK: - Private
    
    private func presentCamera(animated: Bool) {
        guard let presentingViewController = self.presentingViewController else {
            return
        }
        
        let cameraPresenter = CameraPresenter()
        cameraPresenter.delegate = self
        cameraPresenter.presentCamera(from: presentingViewController, with: [.image], animated: animated)
        self.cameraPresenter = cameraPresenter
    }
    
    private func presentPhotoLibray(sourceView: UIView?, sourceRect: CGRect, animated: Bool) {
        guard let presentingViewController = self.presentingViewController else {
            return
        }

        if BWIBuildSettings.shared.useNewPhotosPickerAPI {
            var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
            configuration.filter = .images
            configuration.preferredAssetRepresentationMode = .compatible
            configuration.selection = .default
            configuration.selectionLimit = 1
            configuration.preselectedAssetIdentifiers = []
            
            let pickerViewController = PHPickerViewController(configuration: configuration)
            pickerViewController.delegate = self

            presentingViewController.present(pickerViewController, animated: true)
        } else {
            let mediaPickerPresenter = MediaPickerCoordinatorBridgePresenter(session: self.session, mediaUTIs: [.image], allowsMultipleSelection: false)
            mediaPickerPresenter.delegate = self
            
            mediaPickerPresenter.present(from: presentingViewController, sourceView: sourceView, sourceRect: sourceRect, animated: animated)
            self.mediaPickerPresenter = mediaPickerPresenter
        }
    }
    
    private func removeImage() {
        self.delegate?.singleImagePickerPresenterDidRemoveImage(self)
    }
}

// MARK: - CameraPresenterDelegate
extension SingleImagePickerPresenter: CameraPresenterDelegate {
    
    func cameraPresenter(_ cameraPresenter: CameraPresenter, didSelectImage image: UIImage) {
        if let imageData = image.jpegData(compressionQuality: Constants.jpegCompressionQuality) {
            self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: MXKUTI.jpeg)
        }
    }
    
    func cameraPresenterDidCancel(_ cameraPresenter: CameraPresenter) {
        self.delegate?.singleImagePickerPresenterDidCancel(self)
    }
    
    func cameraPresenter(_ cameraPresenter: CameraPresenter, didSelectVideoAt url: URL) {
        self.delegate?.singleImagePickerPresenterDidCancel(self)
    }
}
// MARK: - MediaPickerCoordinatorBridgePresenterDelegate
extension SingleImagePickerPresenter: MediaPickerCoordinatorBridgePresenterDelegate {
    func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) {
        self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti)
    }
    
    func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideo videoAsset: AVAsset) {
        self.delegate?.singleImagePickerPresenterDidCancel(self)
    }
    
    func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectAssets assets: [PHAsset]) {
        self.delegate?.singleImagePickerPresenterDidCancel(self)
    }
    
    func mediaPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter) {
        self.delegate?.singleImagePickerPresenterDidCancel(self)
    }
}

// MARK: - PHPickerViewControllerDelegate (BWI)
extension SingleImagePickerPresenter: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)
        
        guard let itemProvider = results.first?.itemProvider else {
            self.delegate?.singleImagePickerPresenterDidCancel(self)
            return
        }
        
        // bwi: #5872 workaround for known issue (63426347) selection of images on iOS 15 (simulator)
        let heicId = "public.heic"
        
        if itemProvider.registeredTypeIdentifiers.contains(heicId) {
            itemProvider.loadDataRepresentation(forTypeIdentifier: heicId) { imageData, error in
                DispatchQueue.main.async {
                    if let imageData = imageData, let image = UIImage(data: imageData), let imageData = image.pngData() {
                        let uti = MXKUTI(mimeType: "image/png")
                        self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti)
                    } else {
                        self.delegate?.singleImagePickerPresenterDidCancel(self)
                    }
                }
            }
        } else {
            if itemProvider.canLoadObject(ofClass: UIImage.self) {
                itemProvider.loadObject(ofClass: UIImage.self) { image, error in
                    DispatchQueue.main.async {
                        if let image = image as? UIImage, let imageData = image.pngData() {
                            let uti = MXKUTI(mimeType: "image/png")
                            self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti)
                        } else {
                            self.delegate?.singleImagePickerPresenterDidCancel(self)
                        }
                    }
                }
            } else {
                self.delegate?.singleImagePickerPresenterDidCancel(self)
            }
        }
    }
}
