import APIClient
import ComponentLibrary
import ComposableArchitecture
import Localization
import LyricsClient
import StatsigClient
import SwiftUI
import Utilities

public struct ExpandedPlayerChrome: View {
    @Bindable var store: StoreOf<ExpandedPlayerReducer>
    @State var showSeeMoreChevron: Bool = false
    @State var showGenerateMoreButton: Bool = false
    @State var tooltipOpacity: Double = 0.0
    @State var shouldAnimateClipCards: Bool = false
    @State var lyricsOverlaySize: CGSize = .zero
    @Binding var verticalScrollPosition: CGFloat
    @Environment(\.safeAreaInsets) var safeAreaInsets
    private let tooltipFadeEndThreshold: CGFloat = 50
    private let tooltipFadeStartThreshold: CGFloat = 0
    private let onSeeMoreChevronTapped: () -> Void
    private let opacityAnimation = Animation.timingCurve(0.65, 0, 0.35, 1, duration: 0.15)
    private let positionAnimation = Animation.interpolatingSpring(stiffness: 330, damping: 36).delay(0.5)

    private var chromeBottomPadding: CGFloat { return store.shouldHideControls ? 70 : 100 }

    private var hideSeeMoreChevron: Bool {
        store.showNewSongsTooltip
    }

    private var showingNewSongsTooltip: Bool {
        store.showNewSongsTooltip
    }

    private var chromeContentOpacity: Double {
        showingNewSongsTooltip ? 0.4 : 1.0
    }

    private var chromeContentBrightness: Double {
        showingNewSongsTooltip ? -0.3 : 0
    }

    private var actionBarOpacity: Double {
        showingNewSongsTooltip || store.shouldHideControls ? 0 : 1
    }

    private var seeMoreTooltipOpacity: Double {
        showingNewSongsTooltip ? 0.0 : tooltipOpacity
    }

    private var isChromeInteractionDisabled: Bool {
        showingNewSongsTooltip
    }

    public init(
        store: StoreOf<ExpandedPlayerReducer>,
        verticalScrollPosition: Binding<CGFloat>,
        onSeeMoreChevronTapped: @escaping () -> Void = {}
    ) {
        self.store = store
        self._verticalScrollPosition = verticalScrollPosition
        self.onSeeMoreChevronTapped = onSeeMoreChevronTapped
    }

    private var contentOpacity: Double {
        // Read drag offset from shared state
        @Shared(.inMemory(.omniPlayerDragOffset)) var dragOffset: CGFloat = 0
        @Shared(.inMemory(.isCompactPlayerVisible)) var isCompactPlayerVisible: Bool = false

        let dragWhileExpandedThreshold: CGFloat = 200
        let dragWhileCollapsedThreshold: CGFloat = 600
        let isExpanded = !isCompactPlayerVisible || store.source == .hooksFeed

        // Calculate expansion progress based on current state and drag offset
        let expansionProgress: CGFloat
        if isExpanded {
            // When expanded: dragOffset is positive and increases as we drag down
            expansionProgress = max(0, 1 - (abs(dragOffset) / dragWhileExpandedThreshold))
        } else {
            // When collapsed: dragOffset is negative and decreases as we drag up
            expansionProgress = min(1, abs(dragOffset) / dragWhileCollapsedThreshold)
        }
        return Double(expansionProgress)
    }

    // Make sure we don't show the Create Again / Edit when the controls are hidden
    private var shouldShowGenerateMoreButton: Bool {
        store.shouldShowGenerateMoreButton && !store.shouldHideControls
    }

    public var body: some View {
        VStack(spacing: 14) {
            VStack(spacing: 14) {
                HStack(alignment: .bottom, spacing: 18) {
                    VStack(alignment: .leading, spacing: 8) {
                        timeSyncedComment
                        timeSyncedLyrics
                            // vertical offset since timeSyncedLyrics has it's own fixed vertical sizing
                                .padding(.vertical, -5)
                        if FeatureFlag.hooks.isFeedEnabled && FeatureFlag.hooks.hooksInOmniPlayer {
                            viewHooksButton
                        }
                        clipAndAuthorInfo
                    }
                    .readSize(to: $lyricsOverlaySize)

                    actionBar
                        .opacity(actionBarOpacity)
                        .animation(.easeInOut(duration: 0.3), value: store.shouldHideControls)
                }
                if shouldShowGenerateMoreButton {
                    generateMoreInfo
                        .transition(.asymmetric(
                            insertion: .scale(scale: 0.9).combined(with: .opacity),
                            removal: .identity
                        ))
                        .opacity(store.player.isScrubbing ? 0.2 : 1)
                        .animation(.easeInOut(duration: 0.3), value: store.player.isScrubbing)
                }
            }
            .opacity(chromeContentOpacity)
            .animation(.spring(response: 0.6, dampingFraction: 0.8), value: showingNewSongsTooltip)
            .disabled(isChromeInteractionDisabled)
            .contentShape(Rectangle())
            .onTapGesture {
                store.send(.didTapChromeToToggleControls(verticalOffset: verticalScrollPosition))
            }

            playbar
                .padding(.bottom, chromeBottomPadding)
        }
        .frame(height: UIScreen.height)
        .fixedSize(horizontal: false, vertical: true)
        .opacity(contentOpacity)
        .animation(.easeInOut(duration: 0.2), value: contentOpacity)
        .animation(.easeInOut(duration: 0.2), value: showingNewSongsTooltip)
        .animation(.easeInOut(duration: 0.3), value: store.shouldHideControls)
        .background(Color.clear)
        .contentShape(Rectangle())
        .overlay(alignment: .bottom) {
            if showSeeMoreChevron {
                seeMoreChevron
                    .opacity(seeMoreTooltipOpacity)
                    .animation(.easeInOut(duration: 0.3), value: showingNewSongsTooltip)
            }
        }
        .environment(\.colorScheme, .dark)
        .onAppear {
            withAnimation(positionAnimation) {
                showSeeMoreChevron = true
            }
            withAnimation(opacityAnimation) {
                tooltipOpacity = 1.0
            }
        }
        .onChange(of: verticalScrollPosition) { old, new in
            guard old != new, showSeeMoreChevron else { return }

            let fadeRange = tooltipFadeEndThreshold - tooltipFadeStartThreshold
            let scrollInFadeRange = max(0, verticalScrollPosition - tooltipFadeStartThreshold)
            let newOpacity = max(0, 1.0 - (scrollInFadeRange / fadeRange))

            withAnimation(opacityAnimation) {
                tooltipOpacity = newOpacity
            }

            // If the user scrolled past half of the screen,
            // track the event if we haven't already
            let fiftyPercentThreshold = UIScreen.height * 0.5
            if new >= fiftyPercentThreshold && !store.didTapOrScrollToSeeMore {
                store.send(.didScrollToSeeMore)
            }
        }
        .animation(.snappy, value: store.generationState)
        .task {
            store.send(.task)
        }
    }

    @ViewBuilder
    private var clipAndAuthorInfo: some View {
        ExpandedPlayerClipInfo(
            playerState: store.$player,
            authorTapped: { store.send(.authorTapped(handle: $0)) },
            remixOfTapped: { store.send(.attributionTapped(parentClip: $0)) },
            userMentionTapped: { store.send(.userMentionTapped(handle: $0)) },
            upgradeTapped: { store.send(.upgradeTapped) },
            hideRemixOf: store.shouldHideControls
        )
    }

    @ViewBuilder
    private var generateMoreInfo: some View {
        ExpandedPlayerGenerateMoreInfo(
            generationState: store.generationState,
            generateMoreTapped: { store.send(.generateMoreTapped) },
            editPromptTapped: { store.send(.editPromptTapped) }
        )
    }

    @ViewBuilder
    private var actionBar: some View {
        ExpandedPlayerActionBar(
            playerState: store.$player,
            remixTapped: { store.send(.remixTapped) },
            likeTapped: { store.send(.likeTapped) },
            dislikeTapped: { store.send(.dislikeTapped) },
            commentsTapped: { store.send(.commentsTapped) },
            shareTapped: { store.send(.shareTapped) },
            moreTapped: { store.send(.moreTapped) }
        )
    }

    @ViewBuilder
    private var playbar: some View {
        ExpandedPlayerPlaybar(
            playerState: store.$player,
            clip: store.clip,
            shouldDimProgressBar: showingNewSongsTooltip,
            shouldHideControls: store.shouldHideControls,
            playTapped: { store.send(.playTapped) },
            pauseTapped: { store.send(.pauseTapped) },
            nextTapped: { store.send(.nextTapped) },
            prevTapped: { store.send(.prevTapped) },
            didStartScrubbing: { time in
                store.send(.didStartScrubbing(time))
            },
            didEndScrubbing: { time in
                store.send(.didEndScrubbing(time))
            }
        )
    }

    @ViewBuilder
    private var seeMoreChevron: some View {
        VStack {
            Image.Omniplayer.chevron
                .foregroundColor(.SemanticV2.foregroundTertiaryGlass)
                .frame(width: 24, height: 24)
            Text(L10n.FeatureOmniPlayer.seeMoreTooltip)
                .typographyV1(.monospace.lineHeight(12).inputSans())
                .foregroundStyle(Color.SemanticV2.foregroundTertiaryGlass)
                .padding(.top, -8)
        }
        .shadow(color: Color.SemanticV2.backgroundDarkOverlay, radius: 2)
        .padding(.bottom, 26)
        .transition(
            .asymmetric(
                insertion: .move(edge: .bottom).combined(with: .opacity),
                removal: .opacity
            )
        )
        .onTapGesture {
            onSeeMoreChevronTapped()
            withAnimation(.easeOut(duration: 0.5)) {
                tooltipOpacity = 0.0
            }
        }
        .opacity(store.shouldHideControls ? 0 : 1)
        .animation(.easeInOut(duration: 0.3), value: store.shouldHideControls)
    }

    @ViewBuilder
    private var timeSyncedComment: some View {
        if !store.shouldHideControls, let comment = store.currentTimeSyncedComment, store.isShowingTimeSyncedComment {
            TimeSyncedComment(
                comment,
                onCommentTap: {
                    store.send(.timeSyncedCommentTapped(comment.id))
                }
            )
            .id(comment.id)
            .transition(.asymmetric(
                insertion: .opacity.animation(.easeIn(duration: 0.25)),
                removal: .opacity.animation(.easeOut(duration: 0.25))
            ))
            .opacity(store.state.player.isScrubbing ? 0.2 : 1)
            .animation(.easeInOut(duration: 0.3), value: store.shouldHideControls)
        }
    }

    @ViewBuilder
    private var timeSyncedLyrics: some View {
        if store.shouldShowTimeSyncedLyrics, let lyrics = store.state.lyricsData, lyrics != .empty {
            let fontSize = UIFontMetrics.default.scaledValue(for: 20)
            let lineHeight = fontSize * 1.5
            let maxLines = 2
            let lyricsHeight = lineHeight * CGFloat(maxLines)
            let lyricsWidth = lyricsOverlaySize.width
            let maxLineWidth = lyricsWidth * 0.85

            LyricsOverlay(
                id: store.clip.id.remoteId,
                size: .init(width: lyricsWidth, height: lyricsHeight),
                lyricLines: lyrics.lines,
                startTime: 0,
                endTime: TimeInterval(store.clip.duration),
                elapsedTime: TimeInterval(store.state.player.displayTime.seconds),
                preset: .omniplayer,
                fontResource: .ppNeueMontrealSemiBold,
                fontSize: fontSize,
                fontColor: .white,
                maxLineWidth: maxLineWidth,
                isPaused: !store.state.isPlaying,
                isScrubbing: store.state.player.isScrubbing,
                horizontalAlignment: .leading,
                onTimeUpdate: { _, _, _ in }
            )
            .frame(width: lyricsWidth, height: lyricsHeight)
            .id("lyrics-\(store.clip.id.remoteId)")
            .transition(.asymmetric(
                insertion: .opacity.animation(.easeIn(duration: 0.25)),
                removal: .opacity.animation(.easeOut(duration: 0.25))
            ))
            .opacity(store.state.player.isScrubbing ? 0.2 : 1)
        }
    }

    @ViewBuilder
    private var viewHooksButton: some View {
        if store.clip.hasHook,
           let thumbnailUrl = store.clip.hookPreviewThumbnailUrl
        {
            Button(action: {
                store.send(.viewHookTapped)
            }) {
                HStack(spacing: 5) {
                    RemoteImage(url: thumbnailUrl, fallbackId: store.clip.userId)
                        .clipShape(.rect(cornerRadius: 2))
                        .frame(width: 12, height: 16)
                        .foregroundStyle(Color.SemanticV1.iconPrimary)
                        .shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 0)
                    Text(L10n.FeatureClipDetail.viewHooks)
                        .typographyV1(.body1.size { _ in 12.0 })
                        .foregroundStyle(Color.SemanticV1.textPrimary)
                }
                .padding(.horizontal, 12)
                .padding(.vertical, 5)
                .background {
                    RoundedRectangle(cornerRadius: 36)
                        .fill(Material.ultraThin)
                }
                .padding(.vertical, 4)
            }
        }
    }
}
