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

    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
    ) {
        self.showsCameraControls = showsCameraControls
        self.photoCompletion = photoCompletion
        self.videoCompletion = videoCompletion
        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.cameraCaptureMode = .video
        return controller
    }

    public func updateUIViewController(_ uiViewController: UIImagePickerController, context _: Context) {
        uiViewController.cameraFlashMode = flash ? .on : .off
        uiViewController.cameraCaptureMode = isPhoto ? .photo : .video

        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)
    }

    public class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let photoCompletion: (Result<UIImage, Error>) -> Void
        let videoCompletion: (Result<URL, Error>) -> Void

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

        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))
                }
            }
        }
    }
}
