import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeatureToasts
import Foundation
import Localization
import SwiftUI
import Utilities

public struct HooksSongPickerView: View {
    @Bindable public var store: StoreOf<HooksSongPicker>

    private func isNearingEndOfList(at index: Int) -> Bool {
        return index == store.currentClips.count - 3
    }

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

    public var body: some View {
        ZStack {
            VStack(spacing: 0) {
                navigationHeader
                tabBar
                songList
            }

            if let selectedSong = store.selectedSongForOverlay {
                snippetPlayerOverlay(for: selectedSong)
            }
        }
        .background(Color.SemanticV1.backgroundPrimary)
        .environment(\.colorScheme, .dark)
        .task {
            store.send(.task)
        }
        .sheet(
            item: $store.scope(
                state: \.destination?.mediaPicker,
                action: \.destination.mediaPicker
            )
        ) { _ in
            VideoPicker(
                selectedMovie: $store.selectedMovie.sending(\.setSelectedMovie),
                transferProgress: $store.mediaTransferProgress.sending(\.setMediaTransferProgress),
                onSelectionComplete: {
                    Task { @MainActor in
                        store.send(.dismissMediaPicker)
                    }
                },
                onSelectionCancelled: {
                    store.send(.dismissMediaPicker)
                },
                onError: { _ in
                    store.send(.dismissMediaPicker)
                }
            )
        }
    }

    @ViewBuilder
    private var navigationHeader: some View {
        VStack(spacing: 0) {
            Text(L10n.FeatureHooks.title)
                .typographyV1(.titleText)
                .foregroundColor(.SemanticV1.textPrimary)

            Text(L10n.FeatureHooks.header)
                .typographyV1(.headerText)
                .foregroundColor(.SemanticV1.foregroundInactive)
        }
        .frame(maxWidth: .infinity, alignment: .center)
        .overlay(alignment: .leading) {
            ZStack {
                Circle()
                    .fill(Color.SemanticV1.backgroundSecondary)
                    .frame(width: 40, height: 40)

                Button {
                    store.send(.closeTapped)
                } label: {
                    Image.Icon.close
                        .renderingMode(.template)
                        .resizable()
                        .frame(width: 24, height: 24)
                        .foregroundColor(.SemanticV1.iconPrimary)
                }
            }
            .padding(12)
            .cornerRadius(30)
        }
    }

    @ViewBuilder
    private var tabBar: some View {
        HStack(spacing: 0) {
            ForEach(HooksSongPicker.State.Tab.allCases, id: \.self) { tab in
                Button {
                    switch tab {
                    case .recents:
                        store.send(.recentsSongsTapped)
                    case .public:
                        store.send(.publicSongsTapped)
                    case .liked:
                        store.send(.likedSongsTapped)
                    }
                } label: {
                    ZStack(alignment: .bottom) {
                        Text(tab.title)
                            .typographyV1(.tabText)
                            .foregroundColor(store.selectedTab == tab ? .SemanticV1.textPrimary : .SemanticV1.textSecondary)
                            .padding(.vertical, 12)

                        Rectangle()
                            .fill(Color.SemanticV1.textPrimary)
                            .frame(height: 1)
                            .opacity(store.selectedTab == tab ? 1 : 0)
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.top, 12)
                    .contentShape(Rectangle())
                }
                .buttonStyle(.plain)
            }
        }
    }

    @ViewBuilder
    private var emptyStateView: some View {
        NoResultsView(
            leadingView: { Image.Assets.songsEmptyState2 },
            title: nil,
            message: store.selectedTab.emptyMessageText,
            style: .v1,
            trailingView: {
                PrimaryButtonV1(
                    title: L10n.FeatureHooks.createANewSong,
                    colorCombination: .lightAlways,
                    preferredSize: .createYourOwnSong,
                    action: {
                        store.send(.createNewSongTapped)
                    }
                )
            },
            horizontalPadding: 71,
            verticalPadding: 34,
            verticalSpacing: 16
        )
    }

    @ViewBuilder
    private var songList: some View {
        TabView(selection: selectionBinding) {
            page(for: .recents)
                .tag(0)
            page(for: .public)
                .tag(1)
            page(for: .liked)
                .tag(2)
        }
        .navigationSwipeEnabled()
        .tabViewStyle(.page(indexDisplayMode: .never))
        .indexViewStyle(.page(backgroundDisplayMode: .never))
        .ignoresSafeArea(edges: .bottom)
    }

    private func snippetPlayerOverlay(for snippet: ClipSnippet) -> some View {
        VStack {
            Spacer()

            HooksSnippetPlayerView(
                thumbnail: snippet.clip.imageUrl,
                songTitle: snippet.clip.title,
                songTags: snippet.clip.tags,
                displayTags: snippet.clip.displayTags,
                isPlaying: store.currentlyPlayingClipId == snippet.clip.id && store.isPlayingCurrentClip,
                mode: store.snippetPlayerMode.userFacingMode(
                    forSnippet: snippet,
                    withStore: store
                ),
                clipModel: snippet.clip.highLevelModel
            )
        }
        .ignoresSafeArea(edges: .bottom)
        .animation(.easeInOut(duration: 0.3), value: store.selectedSongForOverlay != nil)
    }
}

// MARK: - Paging Helpers

private extension HooksSongPickerView {
    var selectionBinding: Binding<Int> {
        Binding<Int>(
            get: { index(for: store.selectedTab) },
            set: { newIndex in
                let newTab = tab(for: newIndex)
                switch newTab {
                case .recents:
                    store.send(.recentsSongsTapped)
                case .public:
                    store.send(.publicSongsTapped)
                case .liked:
                    store.send(.likedSongsTapped)
                }
            }
        )
    }

    func index(for tab: HooksSongPicker.State.Tab) -> Int {
        switch tab {
        case .recents: return 0
        case .public: return 1
        case .liked: return 2
        }
    }

    func tab(for index: Int) -> HooksSongPicker.State.Tab {
        switch index {
        case 0: return .recents
        case 1: return .public
        case 2: return .liked
        default: return .recents
        }
    }

    func clips(for tab: HooksSongPicker.State.Tab) -> [ClipSnippet] {
        switch tab {
        case .recents: return store.recentsClips
        case .public: return store.publicClips
        case .liked: return store.likedClips
        }
    }

    @ViewBuilder
    func page(for tab: HooksSongPicker.State.Tab) -> some View {
        let clips = clips(for: tab)

        if clips.isEmpty {
            ZStack {
                Color.clear
                if store.isLoadingMore {
                    GradientSpinner(size: .large,
                                    startColor: Color.SemanticV2.backgroundLightOverlay,
                                    endColor: Color.SemanticV2.omniplayerWhite)
                } else {
                    emptyStateView
                        .padding(.top, 150)
                        .padding(.bottom, 300)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else {
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(Array(clips.enumerated()), id: \.element.clip.id) { index, snippet in
                        SnippetRowView(
                            songTitle: snippet.clip.title,
                            songTags: snippet.clip.tags,
                            displayTags: snippet.clip.displayTags,
                            clipModel: snippet.clip.highLevelModel,
                            thumbnail: snippet.clip.imageUrl,
                            isLiked: snippet.clip.isLiked,
                            isCurrentClip: store.currentlyPlayingClipId == snippet.clip.id,
                            isPlaying: store.isPlayingCurrentClip,
                            onRowTapped: {
                                store.send(.snippetTapped(snippet))
                            }
                        )
                        .onAppear {
                            let nearingEnd = index == clips.count - 3
                            if nearingEnd {
                                store.send(.reachedEndOfSongList(tab: tab))
                            }
                        }
                    }

                    if store.isLoadingMore {
                        GradientSpinner(size: .large,
                                        startColor: Color.SemanticV2.backgroundLightOverlay,
                                        endColor: Color.SemanticV2.omniplayerWhite)
                    }

                    if store.selectedSongForOverlay != nil {
                        Spacer()
                            .frame(height: 92)
                    }
                }
            }
        }
    }
}

private extension HooksSongPicker.State.SnippetPlayerMode {
    func userFacingMode(
        forSnippet snippet: ClipSnippet,
        withStore store: StoreOf<HooksSongPicker>
    ) -> HooksSnippetPlayerMode {
        let playingMode: () -> HooksSnippetPlayerMode = {
            .playing(
                HooksSnippetPlayingModeActions(
                    onPlayPauseTapped: { store.send(.snippetPlayer(.playPauseTapped(snippet))) },
                    onProceedTapped: { store.send(.snippetPlayer(.proceedTapped(snippet))) }
                )
            )
        }
        switch self {
        case .playing:
            return playingMode()
        case .selection:
            return playingMode()
        }
    }
}

private extension TypographyV1 {
    static let titleText: TypographyV1 = .init(
        name: "Title Text",
        size: 18,
        style: .body,
        weight: .ppNeueMontrealMedium,
        lineHeight: 22
    )

    static let headerText: TypographyV1 = .init(
        name: "Header Text",
        size: 12,
        style: .body,
        weight: .ppNeueMontrealMedium,
        lineHeight: 22
    )

    static let tabText: TypographyV1 = .init(
        name: "Tab Text",
        size: 14,
        style: .body,
        weight: .ppNeueMontrealMedium,
        lineHeight: 24
    )

    static let buttonText: TypographyV1 = .init(
        name: "Button Text",
        size: 16,
        style: .body,
        weight: .ppNeueMontrealMedium,
        lineHeight: 24
    )
}

private extension PillButtonSizeV1 {
    static let createYourOwnSong = PillButtonSizeV1(
        typography: .buttonText,
        width: nil,
        maxWidth: .infinity,
        minHeight: 46,
        iconWidth: 16,
        borderRadius: 40,
        padding: EdgeInsets()
    )
}

public extension Color {
    enum Gradient {
        public static let clearBlackLinearGradient: [Color] = [
            Color.clear,
            .SemanticV1.backgroundPrimary.opacity(0.025),
            .SemanticV1.backgroundPrimary.opacity(0.05),
            .SemanticV1.backgroundPrimary.opacity(0.1),
            .SemanticV1.backgroundPrimary.opacity(0.3),
            .SemanticV1.backgroundPrimary.opacity(0.5),
            .SemanticV1.backgroundPrimary.opacity(0.7),
            .SemanticV1.backgroundPrimary.opacity(0.9),
            .SemanticV1.backgroundPrimary,
        ]
    }
}
