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

public struct ExpandedPlayerCollapsibleHeader: View {
    private enum Constants {
        // Space reserved at bottom of screen
        static let headerBottomPadding: CGFloat = 170
        // Height of the header itself
        static let headerHeight: CGFloat = 70
        // Additional padding at top
        static let headerTopPadding: CGFloat = 50
    }

    @Bindable var store: StoreOf<ExpandedPlayerReducer>
    @Binding var verticalScrollPosition: CGFloat
    @State var headerMinY: CGFloat = 0
    private let headerAppearanceThreshold: CGFloat = UIScreen.height - Constants.headerBottomPadding - Constants.headerHeight - Constants.headerTopPadding

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

    private var headerOpacity: Double {
        if verticalScrollPosition < headerAppearanceThreshold {
            return 0 // Completely hidden before threshold
        } else {
            // Fully visible 25px before the see more position
            let maxAdditionalScroll: CGFloat = 25 // Shorter range so it's fully visible before see more
            let additionalScrolled = verticalScrollPosition - headerAppearanceThreshold
            let progress = min(1, additionalScrolled / maxAdditionalScroll)

            // Very aggressive easing - reaches high opacity almost immediately
            return pow(progress, 0.2) // Much steeper curve for faster fade-in
        }
    }

    private var closeButtonOpacity: Double {
        // Start fading out the close button very early and finish fading earlier too
        let closeButtonFadeStartThreshold = headerAppearanceThreshold * 0.2
        let closeButtonFadeEndThreshold = headerAppearanceThreshold * 0.6

        if verticalScrollPosition < closeButtonFadeStartThreshold {
            return 1.0 // Fully visible at the top
        } else if verticalScrollPosition >= closeButtonFadeEndThreshold {
            return 0.0 // Fully invisible well before header starts showing
        } else {
            // Smoothly fade out in between
            let fadeRange = closeButtonFadeEndThreshold - closeButtonFadeStartThreshold
            let progress = (verticalScrollPosition - closeButtonFadeStartThreshold) / fadeRange

            // Apply easing for smooth transition
            return cos(progress * Double.pi / 2) // Cosine gives a nice ease-out effect
        }
    }

    public var body: some View {
        ZStack {
            topHeader
                .opacity(closeButtonOpacity)
                .animation(.snappy(duration: 0.3), value: closeButtonOpacity)

            headerPlaybar
                .glassBackground(shape: .rect, fallbackStyle: .ultraThinMaterial)
                .opacity(headerOpacity)
                .animation(.snappy(duration: 0.3), value: headerOpacity)
        }
        .onGeometryChange(for: CGRect.self) { proxy in proxy.frame(in: .local) } action: {
            headerMinY = $0.minY
        }
        .environment(\.colorScheme, .dark)
    }

    @ViewBuilder
    private var headerPlaybar: some View {
        HStack(spacing: 8) {
            RemoteImage(url: store.clip.largeImageUrl, fallbackId: store.clip.id.remoteId)
                .frame(width: 36, height: 44)
                .clipShape(RoundedRectangle(cornerRadius: 6))
            VStack(alignment: .leading, spacing: 0) {
                headerScrollingTitleBar
                    .foregroundColor(.SemanticV2.foregroundPrimary)
                    .transition(.opacity)
                    .multilineTextAlignment(.leading)
                    .lineLimit(1)
                    .frame(height: 22)
                headerScrollingAuthorBar
                    .foregroundColor(.SemanticV2.foregroundTertiaryGlass)
                    .transition(.opacity)
                    .multilineTextAlignment(.leading)
                    .lineLimit(1)
                    .frame(height: 16)
            }
            Spacer()
            Button {
                if store.player.timeControlStatus == .paused {
                    store.send(.playTapped)
                } else {
                    store.send(.pauseTapped)
                }
            } label: {
                let image = store.player.timeControlStatus == .paused ? Image.Omniplayer.play : Image.Omniplayer.pause
                image
                    .resizable()
                    .frame(width: 24, height: 24)
                    .foregroundColor(.SemanticV2.foregroundPrimary)
                    .padding(8)
                    .background(Circle().fill(Color.SemanticV2.backgroundSmokeDense))
            }
            .contentShape(.rect)
            .buttonStyle(ScaleButtonStyle(scaleAmount: 0.98, minimumOpacity: 1))
        }
        .padding(.bottom, 11)
        .padding(.leading, 12)
        .padding(.trailing, 12)
        .padding(.top, 74)
        .overlay(alignment: .bottom) {
            Rectangle()
                .frame(height: 1)
                .frame(maxWidth: .infinity)
                .foregroundColor(.SemanticV2.backgroundFogDense)
                .offset(y: -0.5)
                .shadow(color: Color.SemanticV2.backgroundSmokeDense, radius: 1, x: 0, y: -1)
        }
    }

    @ViewBuilder
    private var headerScrollingTitleBar: some View {
        MarqueeText(
            text: store.clip.title,
            font: TypographyV1.body1.size { _ in 16.0 }.lineHeight(22.0).uiFont ?? .systemFont(ofSize: 16),
            leftFade: 4,
            rightFade: 4,
            startDelay: 1,
            alignment: .leading
        )
    }

    @ViewBuilder
    private var headerScrollingAuthorBar: some View {
        MarqueeText(
            text: store.clip.displayName,
            font: TypographyV1.body1.size { _ in 14.0 }.lineHeight(16.0).uiFont ?? .systemFont(ofSize: 14),
            leftFade: 4,
            rightFade: 4,
            startDelay: 1,
            alignment: .leading
        )
    }

    @ViewBuilder
    private var topHeader: some View {
        ZStack {
            closeButton

            if store.generationState.isReady && !store.showNewSongsTooltip {
                generationCompletePill
                    .transition(.scale(scale: 0.9).combined(with: .opacity))
            } else if store.player.isPlayingNewGeneration {
                queueIndicator
            }
        }
        .animation(.snappy, value: store.generationState)
        .opacity(store.player.isScrubbing ? 0.2 : 1)
        .animation(.easeInOut(duration: 0.3), value: store.player.isScrubbing)
        .padding(.top, 64)
    }

    @ViewBuilder
    private var generationCompletePill: some View {
        ExpandedPlayerGenerationCompletePill(
            clips: store.generationState.newClips,
            playTapped: { store.send(.playGeneratedClips(store.generationState.newClips)) }
        )
        .frame(maxWidth: .infinity, alignment: .top)
        .animation(.easeInOut(duration: 0.3), value: store.player.isScrubbing)
    }

    // Only visible when the header playbar isn't
    @ViewBuilder
    private var closeButton: some View {
        ActionButton(
            icon: Image.Icon.chevronDown,
            isHighlighted: false,
            label: nil,
            isMore: false,
            tooltip: nil,
            tooltipAccentColor: nil,
            tooltipDismissedAction: nil,
            action: {
                store.send(.delegate(.closeTapped))
            }
        )
        .frame(maxWidth: .infinity, alignment: .topLeading)
        .padding(.leading, 12)
    }

    @ViewBuilder
    private var queueIndicator: some View {
        PillPagingIndicator(
            currentIndex: store.selectedItemIndex,
            totalItems: store.player.queue.count,
            maxVisible: 6,
            color: Color.SemanticV2.foregroundPrimary
        )
    }
}
