import Adamantium
import ComponentLibrary
import Foundation
import Localization
import SwiftUI

struct CreateCameraControlBar: View {
    @ObservedObject var captureCoordinator: CaptureCoordinator
    @State private var isNewDragGestureStarted: Bool = false
    @State var hapticProxy: Int = .zero
    @State var isStartingTouchOnButton: Bool = false

    @Binding var isTapOrHoldInstructionShown: Bool
    @Binding var buttonState: CreateCameraRecordButtonState
    @Binding var completionRatio: CGFloat
    @Binding var cameraPermissionState: CaptureFrameMetaData.CapturePermissionState

    let onMediaPickerTap: () -> Void

    var body: some View {
        VStack {
            Spacer()
            ZStack {
                Group {
                    flipButtonRow
                    libraryButton
                    recordTimerRow
                    mainButtonRow
                }
                .padding(.horizontal, Constants.spacingAround)
            }
            .frame(height: Constants.bottomBarHeight)
            .sensoryFeedbackIfEnabled(.success, trigger: hapticProxy)
            .onAppear {
                isNewDragGestureStarted = false
            }
            .background {
                Color.SemanticV1.backgroundPrimary.opacity(Constants.barOpacity)
            }
            .padding(.top, Constants.spacingAround)
            .overlay(alignment: .top) {
                Text(L10n.FeatureCreateClip.tapOrHold)
                    .typographyV1(.body2)
                    .foregroundStyle(Color.SemanticV1.textInvert)
                    .environment(\.colorScheme, .light)
                    .opacity(isTapOrHoldInstructionShown ? .one : .zero)
                    .animation(.easeOut, value: isTapOrHoldInstructionShown)
            }
            .background {
                Color.clear
            }
        }
    }
}

private extension CreateCameraControlBar {
    enum Constants {
        static let spacingAround: CGFloat = 32.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 {
            Color.clear
                .overlay {
                    recordTimer
                }
            Color.clear
            Color.clear
            Color.clear
        }
    }

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

            Text(buttonState.recordingFormattedTime)
                .typographyV1(.monospace)
                .foregroundStyle(Color.SemanticV1.textOnDark)
        }
        .padding(.horizontal, 12.0)
        .padding(.vertical, 8.0)
        .background {
            Color.SemanticV1.backgroundPrimary.opacity(0.25)
        }
        .clipShape(.rect(cornerRadius: .infinity))
        .opacity(buttonState.isShowingRecordIndicator ? 1.0 : .zero)
        .animation(.easeInOut, value: buttonState.isShowingAncillaryButtons)
    }

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

            Button(
                action: {
                    captureCoordinator.flipCamera()
                },
                label: {
                    Image.Icon.flip
                        .foregroundStyle(Color.SemanticV1.iconOnDark)
                }
            )
            .disabled(!buttonState.isShowingAncillaryButtons)
            .opacity(buttonState.isShowingAncillaryButtons ? 1.0 : .zero)
            .animation(.easeInOut, value: buttonState.isShowingAncillaryButtons)
        }
    }

    @ViewBuilder
    var mainButtonRow: some View {
        HStack {
            Spacer()
            ZStack(alignment: .center) {
                Circle()
                    .fill(Color.SemanticV1.backgroundTertiary.opacity(0.25))
                Circle()
                    .fill(Color.SemanticV1.backgroundPrimary.opacity(0.1))
                    .padding(4.0)
                RoundedRectangle(cornerRadius: isNewDragGestureStarted ? 16.0 : .infinity, style: .continuous)
                    .fill(Color.clear)
                    .background(Image.Assets.shutterButton)
                    .padding(isNewDragGestureStarted ? 10.0 : 8.0)
                    .id(hapticProxy)

                timerCircle

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

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

            Spacer()
        }
    }

    @ViewBuilder
    var libraryButton: some View {
        HStack {
            Button(
                action: onMediaPickerTap,
                label: {
                    Image.Icon.gallery
                        .foregroundStyle(Color.SemanticV1.iconOnDark)
                }
            )
            .disabled(!buttonState.isShowingAncillaryButtons)
            .opacity(buttonState.isShowingAncillaryButtons ? 1.0 : .zero)
            .animation(.easeInOut, value: buttonState.isShowingAncillaryButtons)
            Spacer()
        }
    }

    @ViewBuilder
    var timerCircle: some View {
        Circle()
            .trim(from: 0, to: completionRatio)
            .stroke(
                style: StrokeStyle(
                    lineWidth: 8.0,
                    lineCap: .round
                )
            )
            .foregroundStyle(LinearGradient(colors: [
                Color.SemanticV1.iconLink,
                Color.SemanticV1.iconWarning,
            ], startPoint: .bottomTrailing, endPoint: .topLeading))
            .rotationEffect(.degrees(-90))
            .id(completionRatio)
            .padding(4.0)
    }

    var tapGesture: some Gesture {
        TapGesture()
            .onEnded {
                guard case .readyToRecord = buttonState else { return }
                captureCoordinator.capturePhoto()
            }
    }

    var dragGesture: some Gesture {
        switch buttonState {
        case .disabled, .inactiveLoading:
            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
                }
        }
    }
}
