import AVKit
import SwiftUI

public typealias CaptureViewSignalToEndAction = () -> Void

public struct CaptureView: View {
    @ObservedObject var captureCoordinator: CaptureCoordinator

    let onCapturePhoto: (URL) -> Void
    let onCaptureVideo: (URL) -> Void
    let onCaptureAudio: (URL) -> Void
    let onComposeVideoAndAudio: (URL) -> Void

    public init(_ captureCoordinator: CaptureCoordinator,
                onCapturePhoto: @escaping (URL) -> Void,
                onCaptureVideo: @escaping (URL) -> Void,
                onCaptureAudio: @escaping (URL) -> Void,
                onComposeVideoAndAudio: @escaping (URL) -> Void)
    {
        self.captureCoordinator = captureCoordinator
        self.onCapturePhoto = onCapturePhoto
        self.onCaptureVideo = onCaptureVideo
        self.onCaptureAudio = onCaptureAudio
        self.onComposeVideoAndAudio = onComposeVideoAndAudio
    }

    public var body: some View {
        PreviewMetalViewRepresentable(
            videoCaptureCoordinator: captureCoordinator.videoCaptureCoordinator,
            audioCaptureCoordinator: captureCoordinator.audioCaptureCoordinator,
            previewCoordinator: captureCoordinator.previewCoordinator,
            uniformScale: $captureCoordinator.zoom
        )
        .onAppear {
            videoCapture.start()
        }
        .onDisappear {
            videoCapture.end()
            audioCapture.resetAudioSessionForPlayback()
        }
        .onChange(of: captureCoordinator.capturedVideoURL) { _, newValue in
            guard let newValue else { return }
            onCaptureVideo(newValue)
            guard shouldComposeOnCapture else { return }
            captureCoordinator.composeVideoAndAudioIfAvailable()
        }
        .onChange(of: captureCoordinator.capturedPhotoURL) { _, newValue in
            guard let newValue else { return }
            onCapturePhoto(newValue)
        }
        .onChange(of: captureCoordinator.capturedAudioURL) { _, newValue in
            guard let newValue else { return }
            onCaptureAudio(newValue)
            guard shouldComposeOnCapture else { return }
            captureCoordinator.composeVideoAndAudioIfAvailable()
        }
        .onChange(of: captureCoordinator.composedVideoWithAudio) { _, newValue in
            guard let newValue else { return }
            onComposeVideoAndAudio(newValue)
        }
    }
}

private extension CaptureView {
    var videoCapture: VideoCaptureCoordinator {
        return captureCoordinator.videoCaptureCoordinator
    }

    var audioCapture: AudioCaptureCoordinator {
        return captureCoordinator.audioCaptureCoordinator
    }

    var shouldComposeOnCapture: Bool {
        return captureCoordinator.captureOptions.contains(.shouldComposeOnCapture)
    }
}
