import APIClient
import AVFoundation
import ComponentLibrary
import ComposableArchitecture
import Localization
import SwiftUI
import Utilities

@Reducer
public struct VideoSongArtFullscreenPreview {
    @ObservableState
    public struct State: Equatable {
        @ObservationStateIgnored @ObservedBox public var player: VideoSongArtFullscreenPreviewPlayer.State
        public let clip: Clip
        public let videoAsset: AVAsset
        public let videoGravity: AVLayerVideoGravity
        @Shared(.inMemory(.isPlayingKey)) var isPlaying: Bool = false

        public init(clip: Clip, videoAsset: AVAsset, videoGravity: AVLayerVideoGravity = .resizeAspectFill) {
            self.clip = clip
            self.videoAsset = videoAsset
            self.player = .init(clip: clip)
            self.videoGravity = videoGravity
        }
    }

    @Dependency(\.dismiss) var dismiss

    public enum Action {
        case player(VideoSongArtFullscreenPreviewPlayer.Action)
        case task
        case setupPlayer
        case playPauseToggled
        case restartTapped
        case playTapped
        case pauseTapped
        case dismiss
        case didStartScrubbing(VideoSongArtFullscreenPreviewPlayer.State.ScrubState)
        case didEndScrubbing(VideoSongArtFullscreenPreviewPlayer.State.ScrubState)
    }

    public var body: some Reducer<State, Action> {
        Scope(state: \.player, action: \.player) {
            VideoSongArtFullscreenPreviewPlayer()
        }
        Reduce { state, action in
            switch action {
            case .task:
                return .send(.setupPlayer)

            case .setupPlayer:
                return .send(.player(.setup))

            case .playPauseToggled:
                return .send(state.isPlaying ? .player(.pause) : .player(.play))

            case .restartTapped:
                return .send(.player(.restart))

            case .playTapped:
                return .send(.player(.play))

            case .pauseTapped:
                return .send(.player(.pause))

            case .didStartScrubbing(let scrubState):
                return .send(.player(.binding(.set(\.scrub, scrubState))))

            case .didEndScrubbing(let scrubState):
                return .send(.player(.binding(.set(\.scrub, scrubState))))

            case .dismiss:
                return .concatenate(
                    .send(.player(.teardown)),
                    .run { _ in
                        await dismiss()
                    }
                )

            case .player:
                return .none
            }
        }
    }
}

public struct VideoSongArtFullscreenPreviewScreen: View {
    let store: StoreOf<VideoSongArtFullscreenPreview>

    public init(store: StoreOf<VideoSongArtFullscreenPreview>) {
        self.store = store
    }

    public var body: some View {
        ZStack {
            Color.clear.contentShape(.rect)
                .onTapGesture {
                    store.send(.playPauseToggled)
                }
            VideoSongArtFullscreenPreviewMedia(
                store: store.scope(state: \.player, action: \.player),
                videoAsset: store.videoAsset,
                videoGravity: store.videoGravity
            )
        }
        .overlay {
            overlayGradient
            controls
        }
        .ignoresSafeArea()
        .task {
            store.send(.task)
        }
        .environment(\.colorScheme, .dark)
    }

    private var overlayGradient: some View {
        VStack {
            LinearGradient(
                colors: [
                    .black.opacity(0.6),
                    .black.opacity(.zero),
                    .black.opacity(.zero),
                    .black.opacity(0.2),
                    .black.opacity(0.6),
                ],
                startPoint: .top,
                endPoint: .bottom
            )
        }
    }

    private var controls: some View {
        VStack(spacing: 8) {
            titleBar
            Spacer()
            VideoSongArtFullscreenPreviewPlaybar(
                store: store.scope(state: \.player, action: \.player),
                clip: store.clip,
                restartTapped: { store.send(.restartTapped) },
                playTapped: { store.send(.playTapped) },
                pauseTapped: { store.send(.pauseTapped) },
                didStartScrubbing: { store.send(.didStartScrubbing($0)) },
                didEndScrubbing: { store.send(.didEndScrubbing($0)) }
            )
        }
        .padding(.vertical, 24)
        .padding(.horizontal, 16)
        .padding(.bottom, 8)
        .padding(.top, 40)
        .transition(.opacity)
    }

    @ViewBuilder
    private var titleBar: some View {
        HStack(alignment: .top, spacing: 2) {
            VStack(alignment: .leading, spacing: .zero) {
                scrollingTitleBar
                    .foregroundColor(.SemanticV1.textPrimary)
                    .transition(.opacity)
                    .padding(.top, 4.0)
                    .multilineTextAlignment(.leading)
                    .lineLimit(1)
                ExpandedPlayerAuthorCapsule(
                    avatarImageUrl: store.clip.avatarImageUrl,
                    displayName: store.clip.displayName,
                    authorTapped: { /* Doesn't do anything */ }
                )
                .transition(.opacity)
                .padding(.top, 4.0)
                previewPill
                    .padding(.top, 8.0)
            }
            Spacer()
            ToolbarButton(.close, background: Material.ultraThin) {
                store.send(.dismiss)
            }
        }
        .frame(alignment: .top)
    }

    @ViewBuilder
    private var previewPill: some View {
        Text(L10n.FeatureClipDetail.preview)
            .typographyV1(.body1.size { _ in 12.0 })
            .foregroundColor(Color.SemanticV1.textPrimary)
            .padding(.horizontal, 11)
            .padding(.vertical, 6)
            .background(RoundedRectangle(cornerRadius: 60).fill(.ultraThinMaterial))
            .environment(\.colorScheme, .dark)
    }

    @ViewBuilder
    private var scrollingTitleBar: some View {
        MarqueeText(
            text: store.clip.title,
            font: TypographyV1.headline6.uiFont ?? .systemFont(ofSize: 17),
            leftFade: 0.5,
            rightFade: 0.5,
            startDelay: 2,
            alignment: .leading
        )
    }
}

// Keeps LoopingVideoPlayer and its masking logic in its own component
public struct VideoSongArtFullscreenPreviewMedia: View {
    let store: StoreOf<VideoSongArtFullscreenPreviewPlayer>
    let videoAsset: AVAsset
    let videoGravity: AVLayerVideoGravity

    public var body: some View {
        ZStack {
            RemoteAverageColorView(imageUrl: store.clip.largeImageUrl)
            loopingVideoPlayer
            overlayGradient
        }
    }

    @ViewBuilder
    private var loopingVideoPlayer: some View {
        LoopingVideoPlayer(
            asset: videoAsset,
            restartBeforePlaying: false,
            isPlaying: Binding(
                get: { store.isPlaying },
                set: { _ in }
            ),
            overrideTime: store.scrub?.time,
            videoGravity: videoGravity,
            fallbackView: {
                RemoteImage(url: store.clip.largeImageUrl, fallbackId: store.clip.id.remoteId)
            }
        )
    }

    @ViewBuilder
    private var overlayGradient: some View {
        VStack {
            LinearGradient(
                colors: [
                    .black.opacity(0.6),
                    .black.opacity(0.0),
                    .black.opacity(.zero),
                    .black.opacity(0.3),
                    .black.opacity(0.6),
                ],
                startPoint: .top,
                endPoint: .bottom
            )
        }
    }
}
