//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import AVFoundation
import Photos
import SignalCoreKit
import SignalServiceKit

extension UIViewController {

    public func ows_askForCameraPermissions(callback: @escaping (Bool) -> Void) {
        // Ensure callback is invoked on main thread.
        let threadSafeCallback: (Bool) -> Void = { granted in
            DispatchMainThreadSafe {
                callback(granted)
            }
        }

        guard UIImagePickerController.isSourceTypeAvailable(.camera) || Platform.isSimulator else {
            Logger.error("Camera ImagePicker source not available")
            threadSafeCallback(false)
            return
        }

        let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)

        guard CurrentAppContext().reportedApplicationState != .background else {
            Logger.warn("Skipping camera permissions request when app is in background, relying on previous status \(authorizationStatus.rawValue)")
            threadSafeCallback(authorizationStatus == .authorized)
            return
        }

        let presentSettingsDialog = {
            AssertIsOnMainThread()

            let actionSheet = ActionSheetController(
                title: OWSLocalizedString("MISSING_CAMERA_PERMISSION_TITLE", comment: "Alert title"),
                message: OWSLocalizedString("MISSING_CAMERA_PERMISSION_MESSAGE", comment: "Alert body")
            )
            if let openSettingsAction = AppContextUtils.openSystemSettingsAction(completion: { threadSafeCallback(false) }) {
                actionSheet.addAction(openSettingsAction)
            }
            actionSheet.addAction(ActionSheetAction(
                title: CommonStrings.dismissButton,
                style: .cancel,
                handler: { _ in
                    threadSafeCallback(false)
                }
            ))
            self.presentActionSheet(actionSheet)
        }

        switch authorizationStatus {
        case .denied:
            DispatchMainThreadSafe { presentSettingsDialog() }

        case .authorized:
            threadSafeCallback(true)

        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video, completionHandler: threadSafeCallback)

        case .restricted:
            threadSafeCallback(false)

        @unknown default:
            Logger.error("Unknown AVAuthorizationStatus: \(authorizationStatus)")
            threadSafeCallback(false)
        }
    }

    public func askForCameraPermissions() async -> Bool {
        return await withCheckedContinuation { continuation in
            self.ows_askForCameraPermissions(callback: continuation.resume(returning:))
        }
    }

    public func ows_askForMediaLibraryPermissions(callback: @escaping (Bool) -> Void) {
        // Ensure callback is invoked on main thread.
        let threadSafeCallback: (Bool) -> Void = { granted in
            DispatchMainThreadSafe {
                callback(granted)
            }
        }

        guard CurrentAppContext().reportedApplicationState != .background else {
            Logger.error("Skipping media library permissions request when app is in background.")
            threadSafeCallback(false)
            return
        }

        guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else {
            Logger.error("PhotoLibrary ImagePicker source not available")
            threadSafeCallback(false)
            return
        }

        let presentSettingsDialog = {
            AssertIsOnMainThread()

            let actionSheet = ActionSheetController(
                title: OWSLocalizedString(
                    "MISSING_MEDIA_LIBRARY_PERMISSION_TITLE",
                    comment: "Alert title when user has previously denied media library access"
                ),
                message: OWSLocalizedString(
                    "MISSING_MEDIA_LIBRARY_PERMISSION_MESSAGE",
                    comment: "Alert body when user has previously denied media library access"
                )
            )

            if let openSettingsAction = AppContextUtils.openSystemSettingsAction(completion: { threadSafeCallback(false) }) {
                actionSheet.addAction(openSettingsAction)
            }
            actionSheet.addAction(ActionSheetAction(
                title: CommonStrings.dismissButton,
                style: .cancel,
                handler: { _ in
                    threadSafeCallback(false)
                }
            ))
            self.presentActionSheet(actionSheet)
        }

        let authorizationStatus = PHPhotoLibrary.authorizationStatus()
        switch authorizationStatus {
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization { status in
                switch status {
                case .authorized, .limited:
                    threadSafeCallback(true)
                default:
                    DispatchMainThreadSafe { presentSettingsDialog() }
                }
            }
        case .denied, .restricted:
            DispatchMainThreadSafe { presentSettingsDialog() }

        case .authorized, .limited:
            threadSafeCallback(true)

        @unknown default:
            owsFail("Unknown authorization status \(authorizationStatus)")
        }
    }

    public func ows_askForMicrophonePermissions(callback: @escaping (Bool) -> Void) {
        // Ensure callback is invoked on main thread.
        let threadSafeCallback: (Bool) -> Void = { granted in
            DispatchMainThreadSafe {
                callback(granted)
            }
        }

        // We want to avoid asking for audio permission while the app is in the background,
        // as WebRTC can ask at some strange times. However, if we're currently in a call
        // it's important we allow you to request audio permission regardless of app state.
        guard CurrentAppContext().reportedApplicationState != .background || DependenciesBridge.shared.currentCallProvider.hasCurrentCall else {
            Logger.error("Skipping microphone permissions request when app is in background.")
            threadSafeCallback(false)
            return
        }

        AVAudioSession.sharedInstance().requestRecordPermission(threadSafeCallback)
    }

    public func askForMicrophonePermissions() async -> Bool {
        return await withCheckedContinuation { continuation in
            self.ows_askForMicrophonePermissions(callback: continuation.resume(returning:))
        }
    }

    public func ows_showNoMicrophonePermissionActionSheet() {
        AssertIsOnMainThread()

        let actionSheet = ActionSheetController(
            title: OWSLocalizedString(
                "CALL_AUDIO_PERMISSION_TITLE",
                comment: "Alert title when calling and permissions for microphone are missing"
            ),
            message: OWSLocalizedString(
                "CALL_AUDIO_PERMISSION_MESSAGE",
                comment: "Alert message when calling and permissions for microphone are missing"
            )
        )

        if let openSettingsAction = AppContextUtils.openSystemSettingsAction() {
            actionSheet.addAction(openSettingsAction)
        }
        actionSheet.addAction(OWSActionSheets.dismissAction)
        self.presentActionSheet(actionSheet)
    }
}
