import Adamantium
import AVKit
import ComponentLibrary
import Localization
import SwiftUI
import UIKit

struct CreateCameraV2: View {
    // I want it to create a new capture coordinator every time.
    // This is intentionally detached from CreateClipCameraVideo.State
    // as an easy way to ensure this gets created new everytime.
    // -- One capture coordinator per video/image lifecycle.
    @ObservedObject var captureCoordinator = CaptureCoordinator()

    @State private var isTapOrHoldInstructionShown: Bool = false
    @State private var buttonState: CreateCameraRecordButtonState = .disabled
    @State private var completionRatio: CGFloat = .zero
    @State private var recordingTimeFormattedString: String = ""
    @State private var captureOptions: Set<CaptureOption> = []
    @State private var cameraPermissionState: CaptureFrameMetaData.CapturePermissionState = .requesting

    @AppStorage(.lastCameraFlashToggleMode) var isFlashToggledOn = false

    let onPhotoCapture: (URL) -> Void
    let onVideoCapture: (URL, Float) -> Void // Output URL, Target FPS
    let onMediaPickerTap: () -> Void

    init(onPhotoCapture: @escaping (URL) -> Void,
         onVideoCapture: @escaping (URL, Float) -> Void,
         onMediaPickerTap: @escaping () -> Void)
    {
        self.onPhotoCapture = onPhotoCapture
        self.onVideoCapture = onVideoCapture
        self.onMediaPickerTap = onMediaPickerTap
    }

    var body: some View {
        ZStack {
            Group {
                ZStack {
                    #if targetEnvironment(simulator)
                        Color.black
                            .onAppear {
                                buttonState = .readyToRecord
                            }
                    #else
                        CaptureView(
                            captureCoordinator,
                            onCapturePhoto: { url in
                                onPhotoCapture(url)
                            },
                            onCaptureVideo: { url in
                                onVideoCapture(url, captureCoordinator.recordingFPS)
                            },
                            onCaptureAudio: { _ in },
                            onComposeVideoAndAudio: { _ in }
                        )
                        .onAppear {
                            captureCoordinator.frameUpdateAction = handleFrameUpdates
                        }
                        .onDisappear {
                            captureCoordinator.endVideoRecording()
                        }
                        .overlay {
                            ZStack {
                                Color.SemanticV1.backgroundPrimary
                                    .opacity(cameraPermissionState == .permitted ? .zero : 1.0)
                                switch cameraPermissionState {
                                case .requesting:
                                    ProgressView()
                                        .progressViewStyle(.circular)

                                case .error:
                                    Image.Icon.alert
                                        .foregroundStyle(Color.SemanticV1.textPrimary)

                                case .notPermitted:
                                    VStack {
                                        Image.Assets.noMediaPermissionIcon
                                            .resizable()
                                            .scaledToFit()
                                            .frame(height: 103)
                                        Text(L10n.FeatureCreateClip.askForCameraMicAccess)
                                            .typographyV1(.headline3.neueMontrealMedium())
                                            .foregroundStyle(Color.SemanticV1.textPrimary)
                                            .multilineTextAlignment(.center)
                                        Text(L10n.FeatureCreateClip.allowingCameraAccessReason)
                                            .typographyV1(.body3)
                                            .foregroundStyle(Color.SemanticV1.textPrimary)
                                            .multilineTextAlignment(.center)
                                        Button {
                                            openAppSettings()
                                        } label: {
                                            Text(L10n.FeatureCreateClip.openSettings)
                                                .typographyV1(.body1)
                                                .foregroundStyle(Color.SemanticV1.iconLink)
                                        }
                                        .padding(.top, 32.0)
                                    }
                                    // Intentionally using width here instead of padding
                                    .frame(width: 240.0)
                                    .opacity(0.75)

                                case .permitted:
                                    Color.clear
                                        .onAppear {
                                            guard case .permitted = cameraPermissionState else { return }
                                            isTapOrHoldInstructionShown = true
                                            DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
                                                isTapOrHoldInstructionShown = false
                                            }
                                        }
                                }
                            }
                        }
                    #endif
                }
                .clipShape(.rect(cornerRadius: 16))
                .padding(.top, 12)

                CreateCameraControlBar(
                    captureCoordinator: captureCoordinator,
                    isTapOrHoldInstructionShown: $isTapOrHoldInstructionShown,
                    buttonState: $buttonState,
                    completionRatio: $completionRatio,
                    cameraPermissionState: $cameraPermissionState,
                    onMediaPickerTap: onMediaPickerTap
                )
            }
        }
        .onAppear {
            completionRatio = .zero
            checkAndApplyFlashOnCamera(isFlashToggledOn)
        }
        .onChange(of: isFlashToggledOn) { _, newValue in
            checkAndApplyFlashOnCamera(newValue)
        }
    }
}

private extension CreateCameraV2 {
    enum Constants {
        static let maxRecordingLimit: TimeInterval = 30.0
    }

    func checkAndApplyFlashOnCamera(_ toggleValue: Bool) {
        if toggleValue {
            // Capture options is set so there will
            // only be one option flag in the option set.
            captureCoordinator.captureOptions.insert(.shouldUseFlash)
        } else {
            captureCoordinator.captureOptions.remove(.shouldUseFlash)
        }
    }

    func handleFrameUpdates(
        frameMetaData: CaptureFrameMetaData,
        endRecordingAction: CaptureViewSignalToEndAction
    ) {
        cameraPermissionState = frameMetaData.combinedPermissionState
        guard case .permitted = cameraPermissionState else { return }
        switch frameMetaData.captureRecordingState {
        case .needsSetup:
            buttonState = .inactiveLoading

        case .readyToRecord:
            buttonState = .readyToRecord

        case .isFlaggedForPhotoCaptureOnNextDraw:
            buttonState = .disabled

        case .isRecording(let elapsedTime, _):
            let formattedTime = formatTimeIntervalAsSeconds(elapsedTime)
            buttonState = .recording(formattedTime)
            if elapsedTime >= Constants.maxRecordingLimit {
                endRecordingAction()
                completionRatio = 1.0
                recordingTimeFormattedString = formatTimeIntervalAsSeconds(elapsedTime)
            } else {
                completionRatio = elapsedTime / Constants.maxRecordingLimit
            }

        case .isFlaggedToCompleteRecordingOnNextDraw:
            buttonState = .disabled

        case .isEndingRecording:
            buttonState = .disabled

        case .completed:
            buttonState = .disabled
        }

        /*
            *Photo Lifecycle*
            > needsSetup
                -> readyToRecord
                    -> isFlaggedForPhotoCaptureOnNextDraw
                        -> completed

            *Video Lifecycle*
            > needsSetup
                -> readyToRecord
                    -> isRecording (loops until triggered to end)
                        -> isFlaggedToCompleteRecordingOnNextDraw
                            -> isEndingRecording
                                -> completed

            Note:
            Triggered every frame
            Client responsibility to gate where needed
         */
    }

    func clearAncillaryBarViews() {
        buttonState = .disabled
    }

    func formatTimeIntervalAsSeconds(_ timeInterval: TimeInterval) -> String {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.second]
        formatter.unitsStyle = .abbreviated
        formatter.zeroFormattingBehavior = .dropAll
        formatter.calendar = Calendar.current
        formatter.calendar?.locale = .current
        return formatter.string(from: timeInterval) ?? String(timeInterval)
    }

    func openAppSettings() {
        guard
            let appSettings = URL(string: UIApplication.openSettingsURLString),
            UIApplication.shared.canOpenURL(appSettings)
        else { return }
        UIApplication.shared.open(appSettings)
    }
}
