import APIClient
import ComponentLibrary
import Foundation
import LyricsClient
import SwiftUI

struct HooksCameraCaptureView: View {
    @StateObject var cameraViewModel = HooksCameraViewModel() // TODO: Are these models even sensible?
    let clip: Clip
    let size: CGSize
    let lyricsData: LyricsDataV2
    let startTime: TimeInterval
    let endTime: TimeInterval
    let onAppear: () -> Void
    let pauseSong: () -> Void
    let resetSongToStart: () -> Void
    let onVideoRecordingStart: () -> Void
    let onVideoCapture: (URL, TimeInterval) -> Void
    let onBackButtonTap: () -> Void
    let onMediaPickerTap: () -> Void
    let onSongPillTap: () -> Void

    init(
        clip: Clip,
        size: CGSize,
        lyricsData: LyricsDataV2?,
        startTime: TimeInterval,
        endTime: TimeInterval,
        onAppear: @escaping () -> Void,
        pauseSong: @escaping () -> Void,
        resetSongToStart: @escaping () -> Void,
        onVideoRecordingStart: @escaping () -> Void,
        onVideoCapture: @escaping (URL, TimeInterval) -> Void,
        onMediaPickerTap: @escaping () -> Void,
        onBackButtonTap: @escaping () -> Void,
        onSongPillTap: @escaping () -> Void
    ) {
        self.clip = clip
        self.size = size
        self.lyricsData = lyricsData ?? .empty
        self.onAppear = onAppear
        self.pauseSong = pauseSong
        self.resetSongToStart = resetSongToStart
        self.onVideoRecordingStart = onVideoRecordingStart
        self.onVideoCapture = onVideoCapture
        self.onMediaPickerTap = onMediaPickerTap
        self.onBackButtonTap = onBackButtonTap
        self.onSongPillTap = onSongPillTap
        self.startTime = startTime
        self.endTime = endTime
    }

    var body: some View {
        ZStack {
            Color.clear
            if !cameraViewModel.isShowingTimer {
                if cameraViewModel.isPillActive {
                    LyricsOverlay(
                        id: "overlay-lyrics",
                        size: size,
                        lyricLines: lyricsData.lines,
                        startTime: startTime,
                        endTime: endTime,
                        elapsedTime: cameraViewModel.songElapsedTime,
                        preset: .threeLine,
                        fontResource: .ppNeueMontrealBold,
                        fontSize: 16.0,
                        maxLineWidth: size.width * 0.8,
                        isPaused: false,
                        onTimeUpdate: cameraViewModel.onLyricsTimeUpdate
                    )
                    .background {
                        Color.black.opacity(0.2)
                            .clipShape(.rect(cornerRadius: 16.0))
                    }
                }

                Group {
                    backButton
                    songPill
                }
                .opacity(cameraViewModel.isRecording ? 0.0 : 1.0)
                .animation(.easeInOut(duration: 0.5), value: cameraViewModel.isRecording)
            }

            if !cameraViewModel.isPillActive && !cameraViewModel.isShowingTimer {
                HooksCameraCaptureControlsView(
                    completionRatio: $cameraViewModel.completionRatio,
                    isTapOrHoldInstructionShown: $cameraViewModel.isTapOrHoldInstructionShown,
                    buttonState: $cameraViewModel.buttonState,
                    cameraPermissionState: $cameraViewModel.cameraPermissionState,
                    onStartRecording: {
                        if cameraViewModel.shouldUseTimerDuringRecording {
                            cameraViewModel.send(.startWritingAssetWithTimerDelay)
                        } else {
                            cameraViewModel.send(.startWritingAsset)
                        }
                    },
                    onEndRecording: {
                        cameraViewModel.send(.endWritingAsset)
                    },
                    onFlipCamera: {
                        cameraViewModel.send(.toggleCamera)
                    },
                    onMediaPickerTap: {
                        cameraViewModel.send(.onMediaPickerTap)
                    }
                )

                actionButtonsForLoopingVideoPlayer
            }

            if cameraViewModel.isShowingTimer {
                timerView
            }
        }
        .environment(\.colorScheme, .dark)
        .overlay {
            if case .processing = cameraViewModel.buttonState {
                Color.black.opacity(0.25)
            }
        }
        .background {
            HooksCameraPreviewView(image: $cameraViewModel.currentFrame)
                .clipShape(.rect(cornerRadius: 16))
                .padding(.top, 12)
        }
        .onAppear {
            onAppear()
            cameraViewModel.songStartDate = .now /* Impacts Lyrics */
            cameraViewModel.pauseSong = pauseSong
            cameraViewModel.resetSongToStart = resetSongToStart
            cameraViewModel.onVideoRecordingStart = onVideoRecordingStart
            cameraViewModel.onVideoCapture = onVideoCapture
            cameraViewModel.onMediaPickerTap = onMediaPickerTap
            cameraViewModel.onBackButtonTap = onBackButtonTap
            cameraViewModel.onSongPillTap = onSongPillTap
            cameraViewModel.send(.checkPermissionState)
            cameraViewModel.send(.startCameraPreviews)
        }
        .onDisappear {
            cameraViewModel.send(.cancelCameraPreviews)
        }
    }
}

private extension HooksCameraCaptureView {
    var isOverlayDisabled: Bool {
        return cameraViewModel.isOverlayDisabled
    }

    var backButton: some View {
        VStack {
            HStack {
                Button(
                    action: {
                        if isOverlayDisabled {
                            /* Nothing */
                        } else {
                            cameraViewModel.send(.onBackButtonTap)
                        }
                    },
                    label: {
                        Image.Icon.arrowLeft
                            .resizable()
                            .frame(width: 44, height: 44)
                            .foregroundStyle(Color.SemanticV1.iconOnDark)
                    }
                )
                .padding(4.0)
                .background(.thinMaterial)
                .clipShape(.circle)
                Spacer()
            }
            Spacer()
        }
        .padding(.top, 32)
        .padding(.horizontal, 16)
        .opacity(isOverlayDisabled ? 0.0 : 1.0)
        .animation(.easeInOut(duration: 0.1), value: isOverlayDisabled)
    }

    var songPill: some View {
        VStack {
            HStack {
                Spacer()
                Button(
                    action: {
                        if isOverlayDisabled {
                            /* Nothing */
                        } else {
                            cameraViewModel.onSongPillTap()
                        }
                    },
                    label: {
                        HStack {
                            RemoteImage(
                                url: clip.imageUrl,
                                fallbackId: "1"
                            )
                            .frame(width: 20.0, height: 24.0)
                            .clipShape(.rect(cornerRadius: 8.0))
                            Spacer()
                            Text(clip.title)
                                .typographyV1(.caption)
                                .lineLimit(1)
                                .truncationMode(.tail)
                            Spacer()
                        }
                        .frame(height: 44)
                        .frame(maxWidth: 200.0)
                        .padding(4.0)
                        .padding(.horizontal, 12.0)
                        .foregroundStyle(Color.SemanticV1.iconOnDark)
                    }
                )
                .background(.thinMaterial)
                .clipShape(.rect(cornerRadius: 16.0))
                Spacer()
            }
            Spacer()
        }
        .padding(.top, 32)
        .padding(.horizontal, 16)
        .opacity(isOverlayDisabled ? 0.0 : 1.0)
        .animation(.easeInOut(duration: 0.1), value: isOverlayDisabled)
    }

    var actionButtonsForLoopingVideoPlayer: some View {
        EmptyView()
        // Disabled for now as these buttons aren't working properly due to actor isolation / threading issues.
//        HStack {
//            Spacer()
//            VStack {
//                Spacer()
//                actionButton(
//                    .Icon.lightning,
//                    isSelected: cameraViewModel.shouldUseFlashDuringRecording
//                ) {
//                    cameraViewModel.send(.toggleFlash)
//                }
//                .padding(.bottom, 8.0)
//
//                actionButton(
//                    .Icon.timer,
//                    isSelected: cameraViewModel.shouldUseTimerDuringRecording
//                ) {
//                    cameraViewModel.send(.toggleTimer)
//                }
//
//                Spacer()
//            }
//        }
//        .padding(.horizontal, 16)
    }

    @ViewBuilder
    func actionButton(_ icon: Image, isSelected: Bool, action: @escaping () -> Void) -> some View {
        Button(
            action: {
                action()
            },
            label: {
                ZStack {
                    icon
                        .resizable()
                        .frame(width: 24, height: 24)
                        .foregroundStyle(isSelected ? Color.SemanticV1.textInvert : Color.SemanticV1.iconOnDark)
                        .padding(.all, 8.0)
                }.background {
                    Color.white
                        .opacity(isSelected ? 1.0 : 0.0)
                }
            }
        )
        .background(.thinMaterial)
        .clipShape(.circle)
        .buttonStyle(.plain)
    }

    @ViewBuilder
    var timerView: some View {
        VStack {
            Spacer()
            Text("\(cameraViewModel.timerCount)")
                .typographyV1(.body1.size { _ in 32.0 })
                .foregroundStyle(Color.SemanticV2.foregroundPrimaryOnDark)
            Spacer()
        }
    }
}
