import ComponentLibrary
import Foundation
import Localization
import SwiftUI

struct HooksCameraCaptureControlsView: View {
    @State private var isNewDragGestureStarted: Bool = false
    @State var hapticProxy: Int = .zero
    @State var isStartingTouchOnButton: Bool = false

    @Binding var completionRatio: CGFloat
    @Binding var isTapOrHoldInstructionShown: Bool
    @Binding var buttonState: HooksCameraRecordButtonState
    @Binding var cameraPermissionState: HooksInputPermissionState

    let onStartRecording: () -> Void
    let onEndRecording: () -> Void
    let onFlipCamera: () -> Void
    let onMediaPickerTap: () -> Void

    init(
        completionRatio: Binding<CGFloat>,
        isTapOrHoldInstructionShown: Binding<Bool>,

        buttonState: Binding<HooksCameraRecordButtonState>,
        cameraPermissionState: Binding<HooksInputPermissionState>,
        onStartRecording: @escaping () -> Void,
        onEndRecording: @escaping () -> Void,
        onFlipCamera: @escaping () -> Void,
        onMediaPickerTap: @escaping () -> Void
    ) {
        self._completionRatio = completionRatio
        self._isTapOrHoldInstructionShown = isTapOrHoldInstructionShown
        self._buttonState = buttonState
        self._cameraPermissionState = cameraPermissionState
        self.onStartRecording = onStartRecording
        self.onEndRecording = onEndRecording
        self.onFlipCamera = onFlipCamera
        self.onMediaPickerTap = onMediaPickerTap
    }

    var body: some View {
        ZStack {
            Color.clear

            VStack {
                Spacer()
                ZStack {
                    Group {
                        flipButtonRow
                            .offset(x: isRecording ? 16.0 : 0.0)
                            .animation(.easeOut(duration: 0.5), value: isRecording)
                        libraryButton
                            .offset(x: isRecording ? -16.0 : 0.0)
                            .animation(.easeOut(duration: 0.5), value: isRecording)
                        mainButtonRow
                    }
                    .padding(.horizontal, Constants.spacingAround)
                }
                .frame(height: Constants.bottomBarHeight)
                .padding(.bottom, 24.0)
                .sensoryFeedbackIfEnabled(.success, trigger: hapticProxy)
                .onAppear {
                    isNewDragGestureStarted = false
                }
                .padding(.top, Constants.spacingAround)
                .overlay(alignment: .top) {
                    Text(L10n.FeatureCreateClip.tapToRecord)
                        .typographyV1(.body2)
                        .foregroundStyle(Color.SemanticV1.textInvert)
                        .environment(\.colorScheme, .light)
                        .opacity(isTapOrHoldInstructionShown ? .one : .zero)
                        .animation(.easeOut, value: isTapOrHoldInstructionShown)
                }
                .background {
                    Color.clear
                }
            }
        }
        .environment(\.colorScheme, .dark)
        .overlay {
            VStack {
                ZStack {
                    recordTimerRow
                }
                .frame(height: Constants.bottomBarHeight)

                Spacer()
            }
            .opacity(isRecording ? 1.0 : 0.0)
            .animation(.easeInOut(duration: 0.5).delay(0.25), value: isRecording)
        }
    }
}

private extension HooksCameraCaptureControlsView {
    var isRecording: Bool {
        switch buttonState {
        case .disabled, .inactiveLoading, .readyToRecord:
            return false
        case .recording, .processing:
            return true
        }
    }

    enum Constants {
        static let spacingAround: CGFloat = 64.0
        static let bottomBarHeight: CGFloat = 128.0
        static let barOpacity: CGFloat = 0.2
        static let buttonSize: CGFloat = 64.0
        static let zoomSensitivityScalar: CGFloat = 400.0
        static let zoomMinimumScalar: CGFloat = 1.0
    }

    @ViewBuilder
    var recordTimerRow: some View {
        HStack {
            Spacer()
            recordTimer
            Spacer()
        }
    }

    @ViewBuilder
    var recordTimer: some View {
        HStack {
            Circle()
                .fill(Color.SemanticV1.iconWarning)
                .frame(width: 12.0, height: 12.0)

            Text(buttonState.recordingFormattedTime)
                .typographyV1(.monospace.size { _ in 16.0 })
                .foregroundStyle(Color.SemanticV1.textOnDark)
        }
        .padding(.horizontal, 16.0)
        .padding(.vertical, 12.0)
        .background(.thinMaterial)
        .clipShape(.rect(cornerRadius: .infinity))
        .opacity(buttonState.isShowingRecordIndicator ? 1.0 : .zero)
        .animation(.easeInOut(duration: 0.5), value: buttonState.isShowingAncillaryButtons)
    }

    @ViewBuilder
    var flipButtonRow: some View {
        HStack {
            Spacer()

            Button(
                action: {
                    onFlipCamera()
                },
                label: {
                    Image.Icon.flipCameraV2
                        .resizable()
                        .frame(width: 54, height: 54)
                        .foregroundStyle(Color.SemanticV1.iconOnDark)
                }
            )
            .background(.thinMaterial)
            .disabled(!buttonState.isShowingAncillaryButtons)
            .opacity(buttonState.isShowingAncillaryButtons ? 1.0 : .zero)
            .animation(.easeInOut, value: buttonState.isShowingAncillaryButtons)
            .clipShape(.circle)
        }
    }

    @ViewBuilder
    var libraryButton: some View {
        HStack {
            Button(
                action: onMediaPickerTap,
                label: {
                    Image.Icon.galleryV2
                        .resizable()
                        .frame(width: 54, height: 54)
                        .foregroundStyle(Color.SemanticV1.iconOnDark)
                }
            )
            .background(.thinMaterial)
            .disabled(!buttonState.isShowingAncillaryButtons)
            .opacity(buttonState.isShowingAncillaryButtons ? 1.0 : .zero)
            .animation(.easeInOut, value: buttonState.isShowingAncillaryButtons)
            .clipShape(.circle)
            Spacer()
        }
    }

    @ViewBuilder
    var mainButtonRow: some View {
        HStack {
            Spacer()
            ZStack(alignment: .center) {
                var indicatorSideLength: CGFloat {
                    switch buttonState {
                    case .processing:
                        return 44.0
                    case .disabled, .inactiveLoading:
                        return 44.0
                    case .readyToRecord:
                        return 80.0
                    case .recording:
                        return 28.0
                    }
                }

                var frameCircleSize: CGFloat {
                    switch buttonState {
                    case .processing:
                        return 44.0
                    case .disabled, .inactiveLoading:
                        return 44.0
                    case .readyToRecord:
                        return 100.0
                    case .recording:
                        return 104.0
                    }
                }

                Circle()
                    .fill(.thinMaterial)
                    .frame(width: frameCircleSize, height: frameCircleSize)

                var frameCornerRadius: CGFloat {
                    switch buttonState {
                    case .disabled, .inactiveLoading, .readyToRecord, .processing:
                        return indicatorSideLength / 2.0
                    case .recording:
                        return 8.0
                    }
                }

                recordingRed
                    .frame(width: indicatorSideLength, height: indicatorSideLength)
                    .id(hapticProxy)
                    .clipShape(.rect(cornerRadius: frameCornerRadius))
                    .animation(.easeInOut(duration: 0.2), value: indicatorSideLength)
                    .animation(.easeInOut(duration: 0.1), value: frameCornerRadius)
                    .opacity(buttonState.primaryButtonOpacity)

                if buttonState.isShowingRecordIndicator {
                    timerCircle
                }

                if case .processing = buttonState {
                    ProgressView()
                        .progressViewStyle(.circular)
                }

                TouchesBeganProxy {
                    guard case .readyToRecord = buttonState else { return }
                    isStartingTouchOnButton = true
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.55) {
                        isStartingTouchOnButton = false
                    }

                    isTapOrHoldInstructionShown = false
                    // captureCoordinator.enabledFlashIfNeeded()
                }
            }
            .frame(width: buttonState.size, height: buttonState.size)
            .contentShape(.rect)
            .opacity(buttonState.opacity)
            .animation(.easeOut(duration: 0.1), value: isStartingTouchOnButton)
            .animation(
                .easeInOut(
                    duration: isNewDragGestureStarted ? 0.2 : 0.1
                ),
                value: buttonState.isShowingAncillaryButtons
            )
            .gesture(tapGesture)

            Spacer()
        }
    }

    @ViewBuilder
    var timerCircle: some View {
        Circle()
            .trim(from: 0, to: completionRatio)
            .stroke(
                style: StrokeStyle(
                    lineWidth: 8.0,
                    lineCap: .round
                )
            )
            .foregroundStyle(recordingRed)
            .rotationEffect(.degrees(-90))
            .id(completionRatio)
            .padding(4.0)
    }

    var tapGesture: some Gesture {
        TapGesture()
            .onEnded {
                switch buttonState {
                case .disabled, .inactiveLoading, .processing:
                    break
                case .readyToRecord:
                    onStartRecording()
                case .recording:
                    onEndRecording()
                }
            }
    }

    var dragGesture: some Gesture {
        switch buttonState {
        case .disabled, .inactiveLoading, .processing:
            return DragGesture().onChanged { _ in }.onEnded { _ in }
        case .readyToRecord, .recording:
            return DragGesture(minimumDistance: .zero)
                .onChanged { gesture in
                    var scalar: CGFloat {
                        if gesture.translation.height < .zero {
                            return gesture.translation.height.magnitude / Constants.zoomSensitivityScalar
                        } else {
                            return .zero
                        }
                    }

                    var newScale = Constants.zoomMinimumScalar + scalar
                    newScale = newScale * newScale
                    let clampedZoom = max(newScale, Constants.zoomMinimumScalar)
                    // captureCoordinator.zoom = clampedZoom
                    // captureCoordinator.startVideoRecording()
                }
                .onEnded { _ in
                    isNewDragGestureStarted = false
                    hapticProxy += 1
                    // captureCoordinator.endVideoRecording()
                    // captureCoordinator.zoom = Constants.zoomMinimumScalar
                }
        }
    }

    @ViewBuilder
    var daylightGradient: LinearGradient {
        LinearGradient(
            colors: [
                .init(uiColor: .init(hex: 0xFA4A96)),
                .init(uiColor: .init(hex: 0xFB5939)),
                .init(uiColor: .init(hex: 0xFD9C35)),
            ],
            startPoint: .bottomTrailing,
            endPoint: .topLeading
        )
    }

    @ViewBuilder
    var recordingRed: Color {
        Color(uiColor: .init(hex: 0xFF4156))
    }
}
