import os.signpost
import SwiftUI

private let signposter = OSSignposter(subsystem: Bundle.main.bundleIdentifier!, category: "PerformanceTracking")

struct HooksTabView: View {
    @State private var currentIndex = 0
    @State private var scrollOffset: CGFloat = 0
    // Track the current horizontal page for each vertical feed index
    @State private var horizontalPages: [Int: Int] = [:]
    // Flag to prevent feedback loops when syncing from audio
    @State private var isSyncingFromAudio = false
    // Scroll position tracking (using id-based scrolling, more reliable than GeometryReader)
    @State private var scrollPosition: Int? = nil
    // Task to debounce scroll updates
    @State private var scrollSettleTask: Task<Void, Never>? = nil

    // Cached expensive computed properties
    @State private var cachedGlobalPlaylist: [UploadedSong] = []
    @State private var cachedPlaylistIndexToFeedPosition: [Int: FeedPosition] = [:]

    @EnvironmentObject private var trendingSongManager: TrendingSongManager
    @Environment(AudioManager.self) private var audioManager
    @EnvironmentObject private var appState: AppState
    @Environment(\.scenePhase) private var scenePhase

    // Sample data - replace with your actual data source
    enum FeedItem {
        case video(title: String, videoUrl: String)
        case song(title: String, artwork: String)
        case uploadedSong(_ uploadedSong: UploadedSong)
        
        var id: String {
            switch self {
            case .uploadedSong(let song):
                return song.id
            default:
                fatalError("unhandled feeditem")
            }
        }
    }

    struct FeedItemWithRemixes {
        let mainItem: FeedItem
        let remixes: [FeedItem]
    }
    
    // Cache feedItems to prevent expensive recomputation during scrolling
    @State private var cachedFeedItems: [FeedItemWithRemixes] = []

    private var feedItems: [FeedItemWithRemixes] {
        cachedFeedItems
    }

    private func updateFeedItems() {
        print("🔄 Updating feed items cache")
        cachedFeedItems = trendingSongManager.hooksFeed.map { feedItem in
            FeedItemWithRemixes(
                mainItem: .uploadedSong(feedItem[0]),
                remixes: feedItem[1...].map({ .uploadedSong($0) }))
        }
    }

    // Helper function to extract UploadedSong from FeedItem
    private func extractSong(from feedItem: FeedItem) -> UploadedSong? {
        switch feedItem {
        case .uploadedSong(let song):
            return song
        default:
            return nil
        }
    }

    // Build playlist from a FeedItemWithRemixes (main + remixes)
    private func buildPlaylist(from item: FeedItemWithRemixes) -> [UploadedSong] {
        var playlist: [UploadedSong] = []

        // Add main item
        if let mainSong = extractSong(from: item.mainItem) {
            playlist.append(mainSong)
        }

        // Add all remixes
        for remix in item.remixes {
            if let remixSong = extractSong(from: remix) {
                playlist.append(remixSong)
            }
        }

        return playlist
    }

    // MARK: - Global Playlist Management

    /// Structure to represent a position in the feed
    struct FeedPosition: Equatable {
        let verticalIndex: Int   // Which FeedItemWithRemixes
        let horizontalPage: Int  // Which page within that item (0 = main, 1+ = remixes)
    }

    /// Build a single global playlist containing all songs from all feed items
    /// This is now cached and only rebuilt when feedItems changes
    private func buildGlobalPlaylist() -> [UploadedSong] {
        print("building playlist cache")

        var playlist: [UploadedSong] = []

        for item in feedItems {
            // Add main song
            if let mainSong = extractSong(from: item.mainItem) {
                playlist.append(mainSong)
            }

            // Add all remixes
            for remix in item.remixes {
                if let remixSong = extractSong(from: remix) {
                    playlist.append(remixSong)
                }
            }
        }

        return playlist
    }

    /// Map each playlist index to its position in the feed (vertical + horizontal)
    /// This is now cached and only rebuilt when feedItems changes
    private func buildPlaylistIndexToFeedPosition() -> [Int: FeedPosition] {
        print("building index to position cache")

        var mapping: [Int: FeedPosition] = [:]
        var playlistIndex = 0

        for (verticalIndex, item) in feedItems.enumerated() {
            // Main song is always horizontal page 0
            mapping[playlistIndex] = FeedPosition(verticalIndex: verticalIndex, horizontalPage: 0)
            playlistIndex += 1

            // Remixes are horizontal pages 1, 2, 3, etc.
            for remixIndex in item.remixes.indices {
                mapping[playlistIndex] = FeedPosition(verticalIndex: verticalIndex, horizontalPage: remixIndex + 1)
                playlistIndex += 1
            }
        }

        return mapping
    }

    /// Update cached playlists when feedItems changes
    private func updateCachedPlaylists() {
        cachedGlobalPlaylist = buildGlobalPlaylist()
        cachedPlaylistIndexToFeedPosition = buildPlaylistIndexToFeedPosition()
    }

    /// Get the playlist index for a given feed position
    private func getPlaylistIndex(verticalIndex: Int, horizontalPage: Int) -> Int {
        var playlistIndex = 0

        for (index, item) in feedItems.enumerated() {
            if index == verticalIndex {
                // Found the vertical item, add the horizontal offset
                return playlistIndex + horizontalPage
            }

            // Count songs in this item (main + remixes) and advance
            playlistIndex += 1 + item.remixes.count
        }

        return 0
    }

    var body: some View {
        let _ = Self._printChanges()
        GeometryReader { geo in
            ZStack(alignment: .top) {
                // Black background
                Color.black
                    .ignoresSafeArea()

                ScrollView(.vertical, showsIndicators: false) {
                    LazyVStack(spacing: 0) {
                        ForEach(Array(feedItems.enumerated()), id: \.offset) { index, itemWithRemixes in
                            HorizontalPlayerPager(
                                mainItem: itemWithRemixes.mainItem,
                                remixes: itemWithRemixes.remixes,
                                currentHorizontalPage: Binding(
                                    get: {
                                        horizontalPages[index] ?? 0
                                    },
                                    set: { newValue in
                                        // Only update if value actually changed
                                        let currentValue = horizontalPages[index] ?? 0
                                        if newValue != currentValue {
                                            horizontalPages[index] = newValue
                                        }
                                    }
                                ),
                                verticalIndex: index,
                                getGlobalPlaylistIndex: getPlaylistIndex,
                                bottomSafeArea: geo.safeAreaInsets.bottom
                            )
                            .containerRelativeFrame([.horizontal, .vertical])
                            .id(index)
                        }
                    }
                    .scrollTargetLayout()
                }
                .scrollPosition(id: $scrollPosition)
                .onChange(of: scrollPosition) { oldPosition, newPosition in
                    signposter.emitEvent("onChange(of:scrollPosition)")

                    print("scrollPosition is \(newPosition)")

                    // Debounce: cancel any pending scroll settle task
                    scrollSettleTask?.cancel()

                    // Only handle valid positions and prevent feedback loops
                    guard let newIndex = newPosition, !isSyncingFromAudio else { return }

                    // Don't trigger if we're already at this index
                    guard newIndex != currentIndex else { return }
//
//                    // Start a task that will execute after scrolling settles (100ms delay)
//                    scrollSettleTask = Task {
                        signposter.emitEvent("scrollSettleTask")
//
//                        try? await Task.sleep(nanoseconds: 500_000_000) // 100ms
//
//                        guard !Task.isCancelled else { return }
//
//                        // Scroll has settled, now update playback AND currentIndex
//                        await MainActor.run {
                            guard !isSyncingFromAudio else { return }
                            guard newIndex != currentIndex else { return }

                            // Update currentIndex - this will cause wrapper to render adjacent views
                            currentIndex = newIndex
                            print("📍 Scroll settled at index: \(newIndex)")

                            // Get the global playlist and calculate the starting index
                            let playlist = cachedGlobalPlaylist
                            guard !playlist.isEmpty else { return }

                            // Get the horizontal page for this vertical item
                            let horizontalPage = horizontalPages[newIndex] ?? 0
                            let startIndex = getPlaylistIndex(verticalIndex: newIndex, horizontalPage: horizontalPage)

                            // Play from the global playlist at the calculated index
                            audioManager.playHooksPlaylist(playlist, startingAt: startIndex)
                            print("🎵 Started global hooks playlist at index \(startIndex) (vertical: \(newIndex), horizontal: \(horizontalPage), total: \(playlist.count) songs)")

                            // Preload the first song of the next feed item (for vertical swipes)
                            if newIndex + 1 < feedItems.count {
                                let nextFeedItemFirstSongIndex = getPlaylistIndex(verticalIndex: newIndex + 1, horizontalPage: 0)
                                audioManager.preloadSongs(at: [nextFeedItemFirstSongIndex])
                            }
//                        }
//                    }
                }
                .ignoresSafeArea()
                .scrollTargetBehavior(.paging)
                .scrollBounceBehavior(.basedOnSize)
                // Allow the paged scroll view to extend under the bottom safe area (TikTok-style)
                // while inner controls can add their own safe-area padding as needed.
                .onAppear {
                    print("hooks tab onAppear \(scrollPosition)")
                    // Initialize feed items cache
                    updateFeedItems()
                    // Initialize cached playlists
                    updateCachedPlaylists()
                    // Initialize scroll position to first item
                    if scrollPosition == nil {
                        scrollPosition = 0
                    }
                    playCurrentItemIfPossible()
                }
                .onDisappear {
                    // Clean up any pending scroll settle tasks
                    scrollSettleTask?.cancel()
                }
                .onChange(of: trendingSongManager.hooksFeed.count) { _, _ in
                    // Feed may load asynchronously; update all caches and start playback
                    updateFeedItems()
                    updateCachedPlaylists()
                    playCurrentItemIfPossible()
                }
                .onChange(of: appState.selectedTab) { oldTab, newTab in
                    // When the user navigates back to Hooks, ensure the current item starts playing
                    if newTab == .hooks && oldTab != .hooks {
                        playCurrentItemIfPossible()
                    }
                }
                .onChange(of: audioManager.currentPlaylistIndex) { oldIndex, newIndex in
                    // Sync feed position when audio changes (e.g., from omniplayer)
                    guard audioManager.playbackContext == .hooks else { return }
                    guard !isSyncingFromAudio else { return }

                    // Don't sync if a scroll is in progress (wait for it to settle)
                    if scrollSettleTask != nil && !(scrollSettleTask?.isCancelled ?? true) {
                        return
                    }

                    // Find the feed position for this playlist index
                    if let position = cachedPlaylistIndexToFeedPosition[newIndex] {
                        // Cancel any pending scroll settle task to prevent conflicts
                        scrollSettleTask?.cancel()

                        isSyncingFromAudio = true

                        print("🔄 Syncing feed to audio: playlist index \(newIndex) -> vertical: \(position.verticalIndex), horizontal: \(position.horizontalPage)")

                        // Update horizontal page for the target vertical item
                        horizontalPages[position.verticalIndex] = position.horizontalPage

                        // Scroll to the correct vertical position if needed
                        if currentIndex != position.verticalIndex {
                            currentIndex = position.verticalIndex
                            withAnimation(.easeInOut(duration: 0.3)) {
                                scrollPosition = position.verticalIndex
                            }
                        }

                        // Small delay to allow UI to settle before re-enabling sync
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                            isSyncingFromAudio = false
                        }
                    }
                }
                .onChange(of: scenePhase) { oldPhase, newPhase in
                    // Resume hooks playback when returning from background
                    if newPhase == .active && appState.selectedTab == .hooks {
                        // Check if we were in hooks context but playback was stopped (e.g., from backgrounding)
                        if audioManager.playbackContext == .hooks && !audioManager.isCurrentlyPlaying {
                            print("🔄 Resuming hooks playback after returning from background")
                            playCurrentItemIfPossible()
                        }
                    }
                }

                // Header overlay with fade animation
                VStack {
                    HStack {
                        // SUNO watermark/logo
                        Image("sunologo")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 95, height: 21.85)
                            .opacity(headerOpacity)
                            .animation(.easeInOut(duration: 0.3), value: headerOpacity)

                        Spacer()

                        // Create Hook button
                        Button(action: {
                            print("Create Hook tapped")
                        }) {
                            Text("Create Hook")
                                .font(.custom("PP Neue Montreal", size: 14).weight(.medium))
                                .foregroundColor(.white)
                                .tracking(0.28)
                                .lineSpacing(30)
                                .padding(.horizontal, 14)
                                .frame(height: 44)
                                .glassEffect(.regular, in: RoundedRectangle(cornerRadius: 56))
                        }
                    }
                    .padding(.horizontal, 12)

                    Spacer()
                }
                .allowsHitTesting(headerOpacity > 0.1) // Disable interaction when nearly invisible
            }
        }
    }

    // Calculate opacity based on current video index
    // Shows logo only on first video, hides on second video and beyond
    // Returns to visible when scrolling back to first video
    private var headerOpacity: Double {
        // Show logo only when on first video (index 0)
        // Add animation for smooth fade
        return currentIndex == 0 ? 1.0 : 0.0
    }

    private func playCurrentItemIfPossible() {
        // Only auto-play when Hooks tab is active
        guard appState.selectedTab == .hooks else { return }
        guard !feedItems.isEmpty else { return }

        let playlist = cachedGlobalPlaylist
        guard !playlist.isEmpty else { return }

        let safeIndex = max(0, min(currentIndex, feedItems.count - 1))

        // Determine which horizontal page is active for this vertical item
        let horizontalPage = horizontalPages[safeIndex] ?? 0
        let startIndex = getPlaylistIndex(verticalIndex: safeIndex, horizontalPage: horizontalPage)
        guard startIndex < playlist.count else { return }

        let targetSong = playlist[startIndex]

        // Avoid restarting if this exact hooks song is already playing
        if audioManager.playbackContext == .hooks,
           let current = audioManager.currentlyPlayingSong,
           current.id == targetSong.id,
           audioManager.isCurrentlyPlaying {
            return
        }

        audioManager.playHooksPlaylist(playlist, startingAt: startIndex)
        print("🎵 Auto-started global hooks playlist at index \(startIndex) (vertical: \(safeIndex), horizontal: \(horizontalPage), total: \(playlist.count) songs)")

        // Preload the first song of the next feed item (for vertical swipes)
        if safeIndex + 1 < feedItems.count {
            let nextFeedItemFirstSongIndex = getPlaylistIndex(verticalIndex: safeIndex + 1, horizontalPage: 0)
            audioManager.preloadSongs(at: [nextFeedItemFirstSongIndex])
        }
    }
}
