import APIClient
import ComponentLibrary
import ComposableArchitecture
import SwiftUI

// TODO: localization
// TODO: cleanup
// TODO: DRY with hooks song picker

private extension Clip.HighLevelModelVersion {
    var displayName: String {
        switch self {
        case .previousToV4:
            return "v3.5"
        case .v4:
            return "v4"
        case .v4_5:
            return "v4.5"
        case .v4_5Plus:
            return "v4.5+"
        case .v5:
            return "v5"
        }
    }
}

public struct LibraryClipPickerSheet: View {
    @Bindable public var store: StoreOf<LibraryClipPicker>
    @State private var showMaskFade: Bool = false

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

    public var body: some View {
        ZStack {
            VStack(spacing: 0) {
                dragHandle
                header
                searchBar
                scrollView
            }

            if store.currentlyPlayingClipId != nil {
                miniPlayer
            }
        }
        .modify {
            /// Use default styling on iOS 26+
            if #available(iOS 26.0, *) {
                $0
            } else {
                $0.presentationBackground {
                    Color.FigmaMCP.Semantic.smokeDense
                        .background(.ultraThinMaterial)
                }
            }
        }
        .preferredColorScheme(.dark)
        .task {
            store.send(.task)
        }
        .onDisappear {
            store.send(.onDisappear)
        }
    }

    private var dragHandle: some View {
        Capsule()
            .fill(Color.white.opacity(0.1))
            .frame(width: 32, height: 4)
            .padding(.top, 16)
            .padding(.bottom, 12)
    }

    private var header: some View {
        HStack {
            Text("Library")
                .font(.custom("PP Neue Montreal", size: 28).weight(.semibold))
                .tracking(0.56)
                .foregroundStyle(Color.SemanticV1.textPrimary)

            Spacer()

            Button {
                store.send(.doneTapped)
            } label: {
                Text("Done")
                    .font(.custom("PP Neue Montreal", size: 12).weight(.medium))
                    .tracking(0.24)
                    .foregroundStyle(Color.SemanticV1.alwaysBlack1)
                    .padding(.horizontal, 16)
                    .frame(height: 40)
                    .background(Color.SemanticV1.textPrimary)
                    .clipShape(Capsule())
            }
            .disabled(store.selectedClipId == nil)
            .opacity(store.selectedClipId == nil ? 0.5 : 1.0)
        }
        .padding(.horizontal, 16)
        .padding(.bottom, 12)
    }

    private var searchBar: some View {
        HStack(spacing: 8) {
            SearchBar(
                text: Binding(
                    get: { store.searchText },
                    set: { store.send(.searchTextChanged($0)) }
                ),
                placeholder: "Search"
            )

            Button {

            } label: {
                Image.FigmaMCP.filter
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .foregroundStyle(Color.SemanticV1.textPrimary)
                    .frame(width: 16, height: 16)
            }
            .frame(width: 40, height: 40)
            .background(Color.white.opacity(0.04))
            .clipShape(Circle())

            Button {

            } label: {
                Image.FigmaMCP.thumbsUp
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .foregroundStyle(Color.SemanticV1.textPrimary)
                    .frame(width: 16, height: 16)
            }
            .frame(width: 40, height: 40)
            .background(Color.white.opacity(0.04))
            .overlay(
                Circle()
                    .stroke(Color.white.opacity(0.1), lineWidth: 1)
            )
            .clipShape(Circle())
        }
        .padding(.horizontal, 16)
    }

    private var scrollView: some View {
        ScrollView {
            LazyVStack(spacing: 1) {
                if store.clips.isEmpty && store.isLoadingMore {
                    VStack {
                        Spacer()
                            .frame(height: 100)
                        GradientSpinner(
                            size: .large,
                            startColor: Color.SemanticV2.backgroundLightOverlay,
                            endColor: Color.SemanticV2.omniplayerWhite
                        )
                        Spacer()
                    }
                } else {
                    ForEach(Array(store.clips.enumerated()), id: \.element.clip.id) { index, snippet in
                        songRow(snippet: snippet)
                            .padding(.horizontal, 16)
                            .onAppear {
                                if index == store.clips.count - 3 {
                                    store.send(.reachedEndOfList)
                                }
                            }
                    }

                    if store.isLoadingMore && !store.clips.isEmpty {
                        GradientSpinner(
                            size: .large,
                            startColor: Color.SemanticV2.backgroundLightOverlay,
                            endColor: Color.SemanticV2.omniplayerWhite
                        )
                        .padding(.vertical, 16)
                    }
                }

                if store.currentlyPlayingClipId != nil {
                    Spacer()
                        .frame(height: 100)
                }
            }
        }
        .contentMargins(.top, 16)
        .onScrollGeometryChange(for: CGFloat.self) { proxy in
            proxy.contentOffset.y + proxy.contentInsets.top
        } action: { _, newValue in
            let newShowMaskFade = newValue > 16
            if showMaskFade != newShowMaskFade {
                showMaskFade = newShowMaskFade
            }
        }
        .mask {
            VStack(spacing: 0) {
                LinearGradient(
                    colors: [.clear, .black],
                    startPoint: .top,
                    endPoint: .bottom
                )
                .frame(height: showMaskFade ? 32 : 16)

                Rectangle()
                    .fill(.black)
            }
            .animation(.snappy, value: showMaskFade)
        }
    }

    private func songRow(snippet: ClipSnippet) -> some View {
        let isPlaying = store.currentlyPlayingClipId == snippet.clip.id && store.isPlayingCurrentClip
        let isSelected = store.selectedClipId == snippet.clip.id

        return Button {
            store.send(.clipSelectionToggled(snippet))
        } label: {
            HStack(alignment: .center, spacing: 0) {
                Button {
                    store.send(.clipRowTapped(snippet))
                } label: {
                    ZStack {
                        ZStack {
                            RemoteImage(
                                url: snippet.clip.imageUrl,
                                fallbackId: snippet.clip.id.remoteId,
                                backgroundColor: Color.gray.opacity(0.3)
                            )

                            Color.black.opacity(0.3)
                        }
                        .frame(width: 60, height: 60)
                        .clipShape(RoundedRectangle(cornerRadius: 12))

                        (isPlaying ? Image.FigmaMCP.pause : Image.FigmaMCP.play)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 14, height: 14)
                            .foregroundStyle(Color.SemanticV1.textPrimary)
                    }
                    .frame(width: 60, height: 60)
                }
                .buttonStyle(.plain)

                Spacer().frame(width: 16)

                VStack(alignment: .leading, spacing: 4) {
                    VStack(alignment: .leading, spacing: 0) {
                        HStack(spacing: 4) {
                            Text(snippet.clip.title)
                                .font(.custom("PP Neue Montreal", size: 16).weight(.medium))
                                .tracking(0.32)
                                .foregroundStyle(Color.SemanticV1.textPrimary)
                                .lineLimit(1)

                            Text(snippet.clip.highLevelModel.displayName)
                                .font(.custom("PP Neue Montreal", size: 12))
                                .tracking(0.24)
                                .foregroundStyle(snippet.clip.highLevelModel == .v5 ? Color.SemanticV1.textLink : Color.SemanticV1.textTertiary)
                                .padding(.horizontal, 6)
                                .padding(.vertical, 2)
                                .background(
                                    snippet.clip.highLevelModel == .v5 ?
                                    Color.SemanticV1.textLink.opacity(0.1) :
                                        Color.white.opacity(0.04)
                                )
                                .clipShape(RoundedRectangle(cornerRadius: snippet.clip.highLevelModel == .v5 ? 34 : 14))
                        }

                        Text(snippet.clip.tags)
                            .font(.custom("PP Neue Montreal", size: 12))
                            .tracking(0.24)
                            .foregroundStyle(Color.SemanticV1.textSecondary)
                            .lineLimit(1)
                    }

                    HStack(spacing: 4) {
                        Text(formatDuration(snippet.clip.duration))
                            .font(.custom("PP Neue Montreal", size: 12))
                            .tracking(0.24)
                            .foregroundStyle(Color.white.opacity(0.2))

                        if snippet.clip.isLiked {
                            Text("·")
                                .font(.custom("PP Neue Montreal", size: 12))
                                .tracking(0.24)
                                .foregroundStyle(Color.white.opacity(0.2))

                            HStack(spacing: 8) {
                                Image.FigmaMCP.thumbsUp
                                    .frame(width: 11, height: 11)
                                    .foregroundStyle(Color.SemanticV1.textPrimary)

                                Text("Liked")
                                    .font(.custom("PP Neue Montreal", size: 12))
                                    .tracking(0.24)
                                    .foregroundStyle(Color.SemanticV1.textPrimary)
                            }
                        }
                    }
                }
                .frame(width: 255, alignment: .leading)

                Spacer()

                ZStack {
                    if isSelected {
                        Image.FigmaMCP.success
                            .resizable()
                            .foregroundStyle(Color(hex: "#F7F4EF"))
                    } else {
                        Image.FigmaMCP.radioButtonOutline
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .foregroundStyle(Color(hex: "#6A6A72"))
                    }
                }
                .frame(width: 24, height: 24)
            }
            .contentShape(.rect)
        }
        .buttonStyle(.plain)
        .padding(.vertical, 8)
        .frame(maxWidth: .infinity)
    }

    private func formatDuration(_ duration: Double) -> String {
        let minutes = Int(duration) / 60
        let seconds = Int(duration) % 60
        return String(format: "%d:%02d", minutes, seconds)
    }

    private var miniPlayer: some View {
        VStack {
            Spacer()

            VStack(spacing: 0) {
                GeometryReader { geometry in
                    ZStack(alignment: .leading) {
                        Rectangle()
                            .fill(Color.white.opacity(0.1))
                            .frame(height: 2)

                        if let playingSnippet = store.clips.first(where: { $0.clip.id == store.currentlyPlayingClipId }) {
                            let progress = playingSnippet.clip.duration > 0 ?
                                CGFloat(store.currentTime / playingSnippet.clip.duration) : 0

                            Rectangle()
                                .fill(Color.SemanticV1.textPrimary)
                                .frame(width: geometry.size.width * min(1.0, max(0, progress)), height: 2)
                        }
                    }
                }
                .frame(height: 2)

                HStack(alignment: .center, spacing: 10) {
                    if let playingSnippet = store.clips.first(where: { $0.clip.id == store.currentlyPlayingClipId }) {
                        RemoteImage(
                            url: playingSnippet.clip.imageUrl,
                            fallbackId: playingSnippet.clip.id.remoteId,
                            backgroundColor: Color.gray.opacity(0.3)
                        )
                        .frame(width: 44, height: 44)
                        .clipShape(RoundedRectangle(cornerRadius: 6))

                        VStack(alignment: .leading, spacing: 2) {
                            Text(playingSnippet.clip.title)
                                .font(.custom("PP Neue Montreal", size: 14).weight(.medium))
                                .tracking(0.28)
                                .foregroundStyle(Color.SemanticV1.textPrimary)
                                .lineLimit(1)

                            Text(playingSnippet.clip.userId)
                                .font(.custom("PP Neue Montreal", size: 14))
                                .tracking(0.56)
                                .foregroundStyle(Color(hex: "#BAB4B1"))
                                .lineLimit(1)
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)

                        HStack(spacing: 20) {
                            Button {
                                store.send(.clipRowTapped(playingSnippet))
                            } label: {
                                (store.isPlayingCurrentClip ? Image.FigmaMCP.pause : Image.FigmaMCP.play)
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 24, height: 24)
                                    .foregroundStyle(Color.SemanticV1.textPrimary)
                            }
                        }
                    }
                }
                .padding(.horizontal, 16)
                .padding(.top, 12)
                .padding(.bottom, 34)
            }
            .background(
                Color(hex: "#363030").opacity(0.3)
                    .background(.ultraThinMaterial)
            )
        }
        .ignoresSafeArea(edges: .bottom)
    }
}

// MARK: - Preview

#Preview {
    @Previewable @State var isPresented: Bool = false

    Button("toggle") {
        isPresented.toggle()
    }
    .sheet(isPresented: $isPresented) {
        LibraryClipPickerSheet(
            store: Store(initialState: LibraryClipPicker.State()) {
                LibraryClipPicker()
            }
        )
        .presentationDetents([.large])
    }
    .task {
        try? await Task.sleep(for: .seconds(0.1))
        guard !Task.isCancelled else { return }
        isPresented = true
    }
}
