import SwiftUI
import UIKit
import UniformTypeIdentifiers

public struct CameraView: UIViewControllerRepresentable {
    let showsCameraControls: Bool
    @Binding var flash: Bool
    @Binding var didTapCapture: Bool
    @Binding var didTapRecord: Bool
    @Binding var didTapFlipCamera: Bool
    @Binding var isPhoto: Bool

    let photoCompletion: (Result<UIImage, Error>) -> Void
    let videoCompletion: (Result<URL, Error>) -> Void
    let cancelCompletion: () -> Void

    enum CameraViewError: Error {
        case unableToLoadImage
    }

    public init(
        showsCameraControls: Bool = false,
        flash: Binding<Bool> = .constant(false),
        didTapCapture: Binding<Bool> = .constant(false),
        didTapRecord: Binding<Bool> = .constant(false),
        didTapFlipCamera: Binding<Bool> = .constant(false),
        isPhoto: Binding<Bool> = .constant(true),
        photoCompletion: @escaping (Result<UIImage, Error>) -> Void,
        videoCompletion: @escaping (Result<URL, Error>) -> Void,
        cancelCompletion: @escaping () -> Void = {}
    ) {
        self.showsCameraControls = showsCameraControls
        self.photoCompletion = photoCompletion
        self.videoCompletion = videoCompletion
        self.cancelCompletion = cancelCompletion
        self._flash = flash
        self._isPhoto = isPhoto
        self._didTapCapture = didTapCapture
        self._didTapRecord = didTapRecord
        self._didTapFlipCamera = didTapFlipCamera
    }

    public func makeUIViewController(context: Context) -> UIImagePickerController {
        let controller = UIImagePickerController()
        controller.sourceType = .camera
        controller.delegate = context.coordinator
        controller.showsCameraControls = showsCameraControls
        controller.cameraDevice = .rear
        controller.mediaTypes = [UTType.movie.identifier, UTType.image.identifier]

        controller.modalPresentationStyle = .fullScreen

        return controller
    }

    public func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
        if context.coordinator.lastFlashMode != flash {
            uiViewController.cameraFlashMode = flash ? .on : .off
            context.coordinator.lastFlashMode = flash
        }

        if context.coordinator.lastIsPhoto != isPhoto {
            uiViewController.cameraCaptureMode = isPhoto ? .photo : .video
            context.coordinator.lastIsPhoto = isPhoto
        }

        if didTapCapture {
            uiViewController.takePicture()
        }

        if didTapRecord {
            uiViewController.startVideoCapture()
        } else {
            uiViewController.stopVideoCapture()
        }

        if didTapFlipCamera {
            let isFront = uiViewController.cameraDevice == .front
            uiViewController.cameraDevice = isFront ? .rear : .front
        }
    }

    public func makeCoordinator() -> Coordinator {
        Coordinator(photoCompletion: photoCompletion, videoCompletion: videoCompletion, cancelCompletion: cancelCompletion)
    }

    public class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let photoCompletion: (Result<UIImage, Error>) -> Void
        let videoCompletion: (Result<URL, Error>) -> Void
        let cancelCompletion: () -> Void
        var lastFlashMode: Bool?
        var lastIsPhoto: Bool?

        init(photoCompletion: @escaping (Result<UIImage, Error>) -> Void, videoCompletion: @escaping (Result<URL, Error>) -> Void, cancelCompletion: @escaping () -> Void) {
            self.photoCompletion = photoCompletion
            self.videoCompletion = videoCompletion
            self.cancelCompletion = cancelCompletion
        }

        public func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            DispatchQueue.main.async { [weak self] in
                if let image = (info[.editedImage] ?? info[.originalImage]) as? UIImage {
                    self?.photoCompletion(.success(image))
                } else if let url = info[.mediaURL] as? URL {
                    self?.videoCompletion(.success(url))
                } else {
                    self?.photoCompletion(.failure(CameraViewError.unableToLoadImage))
                }
            }
        }

        public func imagePickerControllerDidCancel(_: UIImagePickerController) {
            DispatchQueue.main.async { [weak self] in
                self?.cancelCompletion()
            }
        }
    }
}
