//
//  ContentView.swift
//  vibes
//
//  Created by Yamill Vallecillo on 6/30/25.
//

import SwiftUI
import FirebaseAuth
import NukeUI

enum MainDestination {
    case create
    case lyricsEditor(lyrics: LyricsStructure, songTitle: String)
    case library
    case workspaces
    case workspacesWithSelection(workspaceId: String)
    case workspace(workspaceTitle: String?, workspaceImageId: String?, chatSongs: [Song])
    case workspaceChat(workspaceTitle: String?, workspaceImageId: String?, chatSongs: [Song])
    case createView
    case artists
}

extension MainDestination: Hashable {
    func hash(into hasher: inout Hasher) {
        switch self {
        case .create:
            hasher.combine(0)
        case .lyricsEditor(let lyrics, let songTitle):
            hasher.combine(1)
            hasher.combine(lyrics)
            hasher.combine(songTitle)
        case .library:
            hasher.combine(2)
        case .workspaces:
            hasher.combine(3)
        case .workspacesWithSelection(let workspaceId):
            hasher.combine(4)
            hasher.combine(workspaceId)
        case .workspace(let workspaceTitle, let workspaceImageId, let chatSongs):
            hasher.combine(5)
            hasher.combine(workspaceTitle)
            hasher.combine(workspaceImageId)
            hasher.combine(chatSongs)
        case .workspaceChat(let workspaceTitle, let workspaceImageId, let chatSongs):
            hasher.combine(6)
            hasher.combine(workspaceTitle)
            hasher.combine(workspaceImageId)
            hasher.combine(chatSongs)
        case .createView:
            hasher.combine(7)
        case .artists:
            hasher.combine(8)
        }
    }
}

extension MainDestination: Equatable {
    static func == (lhs: MainDestination, rhs: MainDestination) -> Bool {
        switch (lhs, rhs) {
        case (.create, .create), (.library, .library), (.workspaces, .workspaces), (.createView, .createView), (.artists, .artists):
            return true
        case (.workspacesWithSelection(let lId), .workspacesWithSelection(let rId)):
            return lId == rId
        case (.lyricsEditor(let lLyrics, let lTitle), .lyricsEditor(let rLyrics, let rTitle)):
            return lLyrics == rLyrics && lTitle == rTitle
        case (.workspace(let lTitle, let lImageId, let lSongs), .workspace(let rTitle, let rImageId, let rSongs)):
            return lTitle == rTitle && lImageId == rImageId && lSongs == rSongs
        case (.workspaceChat(let lTitle, let lImageId, let lSongs), .workspaceChat(let rTitle, let rImageId, let rSongs)):
            return lTitle == rTitle && lImageId == rImageId && lSongs == rSongs
        default:
            return false
        }
    }
}

struct ContentView: View {
    let tabBarState = TabBarState()
    
    @EnvironmentObject private var appState: AppState
    @State private var showCreateSheet = false
    @State private var toastVisible = false
    @State private var navigationPath = NavigationPath()
    @State private var showChatView = false
    @State private var showTweaksSheet = false
    @ObservedObject private var generationManager = SongGenerationManager.shared
    @StateObject private var workspaceManager = WorkspaceManager.shared
    @StateObject private var tweaksManager = TweaksManager.shared
    @EnvironmentObject private var librarySongManager: LibrarySongManager
    @Environment(AudioManager.self) private var audioManager

    var body: some View {
        let _ = Self._printChanges()
        ZStack {
            // App background color
            Color.black
                .ignoresSafeArea()
                .zIndex(-1)

            if tweaksManager.isLiquidGlassTabBarEnabled {
                backgroundContent.zIndex(0)
            } else {
                VStack(spacing: 0) {
                    backgroundContent.zIndex(0)
                    BottomNavBar(selectedTab: $appState.selectedTab, showCreateSheet: $showCreateSheet)
                }
            }
            
            // Now Playing Card - floating above content (hide on hooks tab)
            if !tweaksManager.isLiquidGlassTabBarEnabled,
               let currentSong = audioManager.currentlyPlayingSong,
               appState.selectedTab != .hooks {
                VStack {
                    Spacer()
                    
                    NowPlayingCard(
                        metadata: AudioMetadata(
                            title: currentSong.name,
                            artistName: currentSong.artistName,
                            playCount: "", // Not used anymore
                            artworkName: currentSong.imageURL != nil ? currentSong.imageURL! : "Artwork/2"
                        ),
                        isPlaying: audioManager.isCurrentlyPlaying,
                        onPlayPause: {
                            audioManager.togglePlayback(song: currentSong)
                        },
                        onNext: {
                            print("not implemented")
                        },
                        onCardTap: {
                            appState.showOmniplayer = true
                        }
                    )
                    .padding(.horizontal, 8) // 8px edge padding as requested
                    .padding(.bottom, 64) // Above nav bar (56px nav + 8px padding)
                }
                .transition(.move(edge: .bottom).combined(with: .opacity))
                .animation(.easeInOut(duration: 0.3), value: audioManager.currentlyPlayingSong?.id)
            }


            // Create sheet overlay
            if showCreateSheet {
                Color.black.opacity(0.5)
                    .ignoresSafeArea()
                    .zIndex(1)
            }

            // Global Loading Toast - appears on all tabs
            if generationManager.showToast && toastVisible {
                VStack {
                    LoadingToast()
                        .padding(.horizontal, 16)
                        .padding(.top, 8)
                        .onTapGesture {
                            // Open workspace when toast is tapped
                            generationManager.openWorkspace()
                        }
                    Spacer()
                }
                .transition(.move(edge: .top).combined(with: .opacity))
                .animation(.easeOut(duration: 0.4), value: toastVisible)
                .zIndex(2)
            }
        }
        .onChange(of: showCreateSheet) { oldValue, newValue in
            if newValue && !oldValue {
                showChatView = true
                showCreateSheet = false // Reset the binding
            }
        }
        .fullScreenCover(isPresented: $showChatView) {
            ChatView(
                navigationPath: $navigationPath,
                onDismiss: {
                    showChatView = false
                }
            )
        }
        .onChange(of: generationManager.showToast) { _, newValue in
            if newValue {
                // Delay the toast appearance by 0.3s and then slide it in from the top
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    withAnimation(.easeOut(duration: 0.4)) {
                        toastVisible = true
                    }
                }
            } else {
                // Hide immediately when showToast becomes false
                toastVisible = false
            }
        }
        .sheet(isPresented: $appState.showTweaksSheet) {
            TweaksSheet(tweaksManager: tweaksManager)
        }
        .onShake {
            appState.showTweaksSheet = true
        }
        .environmentObject(tweaksManager)
    }

    // Background content that stays visible behind the sheet
    private var backgroundContent: some View {
        ZStack {
            // Background color
            Constants.BackgroundPrimary
                .ignoresSafeArea()

            if tweaksManager.isLiquidGlassTabBarEnabled {
                TabView(selection: $appState.selectedTab) {
                    Tab(value: .hooks) {
                        HooksTabView()
                            .environmentObject(tabBarState)
                    } label: {
                        VStack {
                            Image("Icon/hooks")
                                .renderingMode(.template)
                            Text("Home")
                        }
                    }
                    
                    Tab(value: .explore) {
                        ExploreTabView()
                    } label: {
                        VStack {
                            Image("Icon/explore")
                                .renderingMode(.template)
                            Text("Explore")
                        }
                    }
                    
                    Tab(value: .library) {
                        LibraryTabView()
                    } label: {
                        VStack {
                            Image("Icon/library")
                                .renderingMode(.template)
                            Text("Library")
                        }
                    }
                    
                    Tab(value: .profile) {
                        ProfileTabView()
                    } label: {
                        VStack {
                            Image("Icon/user")
                                .renderingMode(.template)
                            Text("Profile")
                        }
                    }
                    
                    Tab(value: .create, role: .search) {
                        Text("Create")
                    } label: {
                        Image("Icon/create")
                            .renderingMode(.template)
                        Text("Create")
                    }
                }
                .onChange(of: appState.selectedTab) { oldTab, newTab in
                    while !navigationPath.isEmpty {
                        navigationPath.removeLast()
                    }
                    // When leaving hooks, stop hooks playback and restore primary snapshot
                    if oldTab == .hooks && newTab != .hooks && newTab != .create {
                        audioManager.exitHooksContextAndRestorePrimary(autoResume: false)
                    }
                    // If entering hooks while primary playback is active, it will be snapshotted when hooks playback starts
                    if newTab == .create {
                        showCreateSheet.toggle()
                        appState.selectedTab = oldTab
                    }
                }
                .tint(Constants.Colors.Accent.brand)
                .tabViewBottomAccessory {
                    NowPlayingTabAccessory()
                        .environmentObject(appState)
                        .environmentObject(tabBarState)
                }
                .tabBarMinimizeBehavior(tweaksManager.isLiquidGlassCollapsedTabBarEnabled ? .onScrollDown : .never)
            } else {
                ZStack {
                    VStack(spacing: 0) {
                        // Main content area - keep all tabs alive, just hide/show them
                        ZStack {
                            HooksTabView()
                                .opacity(appState.selectedTab == .hooks ? 1 : 0)
                                .zIndex(appState.selectedTab == .hooks ? 1 : 0)

                            ExploreTabView(navigationPath: $navigationPath)
                                .opacity(appState.selectedTab == .explore ? 1 : 0)
                                .zIndex(appState.selectedTab == .explore ? 1 : 0)

                            LibraryTabView()
                                .opacity(appState.selectedTab == .library ? 1 : 0)
                                .zIndex(appState.selectedTab == .library ? 1 : 0)

                            ProfileTabView()
                                .opacity(appState.selectedTab == .profile ? 1 : 0)
                                .zIndex(appState.selectedTab == .profile ? 1 : 0)
                        }
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .animation(nil, value: appState.selectedTab)
                    }
                    .onChange(of: appState.selectedTab) { oldTab, newTab in
                        while !navigationPath.isEmpty {
                            navigationPath.removeLast()
                        }
                        // When leaving hooks, stop hooks playback and restore primary snapshot
                        if oldTab == .hooks && newTab != .hooks && newTab != .create {
                            audioManager.exitHooksContextAndRestorePrimary(autoResume: false)
                        }
                    }
                }
            }
        }
        .sheet(isPresented: $appState.showOmniplayer) {
            if !audioManager.currentPlaylist.isEmpty {
                // Convert UploadedSong array to MusicTrack array for Omniplayer
                let musicTracks = audioManager.currentPlaylist.map { song in
                    MusicTrack(
                        id: song.id,
                        title: song.name,
                        artist: song.artistName,
                        artworkURL: song.imageURL?.isEmpty == false ? song.imageURL! : "Artwork/2",
                        createdAt: song.createdAt,
                        audioURL: song.audioURL ?? "",
                        duration: 180, // Default duration, could be enhanced
                        likeCount: 0,
                        commentCount: 0,
                        localArtworkName: (song.imageURL?.hasPrefix("http") != true && song.imageURL?.isEmpty == false) ? song.imageURL : nil,
                        rootRemixSongId: song.rootRemixSongId,
                        isCreatedByCurrentUser: song.isCreatedByCurrentUser
                    )
                }

                Omniplayer(
                    songs: musicTracks,
                    initialSongIndex: audioManager.currentPlaylistIndex,
                    onPlayPause: { isPlaying in
                        if let currentSong = audioManager.currentlyPlayingSong {
                            audioManager.togglePlayback(song: currentSong)
                        }
                    },
                    onSeek: { time in
                        audioManager.seekToProgress(time / audioManager.progress.totalDuration) // Convert to progress (0-1)
                    },
                    onSongChange: { song, index in
                        // Check if this is a new song (not in current playlist)
                        let currentPlaylistIds = audioManager.currentPlaylist.map { $0.id }
                        if !currentPlaylistIds.contains(song.id) {
                            // This is a new song (like original song from remix), create new playlist
                            let newUploadedSong = UploadedSong(
                                id: song.id,
                                name: song.title,
                                artistName: song.artist,
                                artistId: song.artist,
                                createdAt: song.createdAt,
                                imageURL: song.artworkURL.hasPrefix("http") ? song.artworkURL : nil,
                                audioURL: song.audioURL,
                                originalPrompt: nil,
                                rewrittenPrompt: nil,
                                rootRemixSongId: song.rootRemixSongId,
                                isCreatedByCurrentUser: song.isCreatedByCurrentUser
                            )
                            audioManager.playPlaylist([newUploadedSong], startingAt: 0)
                        } else {
                            // Song is in current playlist, play from that index
                            audioManager.playFromPlaylist(at: index)
                        }
                    },
                    onDismiss: {
                        appState.showOmniplayer = false
                    }
                )
                .id(audioManager.currentPlaylist.map { $0.id }.joined(separator: "-"))
                .presentationBackground(.clear)
                .presentationDragIndicator(.visible)
                .presentationBackgroundInteraction(.enabled)
            }
        }
    }
}

/// Extracted now playing accessory so we can observe the tabViewBottomAccessoryPlacement
/// environment and propagate whether it's expanded/inline back into AppState.
private struct NowPlayingTabAccessory: View {
    @Environment(\.tabViewBottomAccessoryPlacement) private var placement
    @EnvironmentObject private var appState: AppState
    @EnvironmentObject private var tabBarState: TabBarState
    @Environment(AudioManager.self) private var audioManager

    var body: some View {
        Group {
            if let currentSong = audioManager.currentlyPlayingSong {
                HStack {
                    ZStack {
                        Circle()
                            .foregroundStyle(.clear)
                            .frame(width: 34, height: 34)
                            .overlay {
                                ZStack {
                                    LazyImage(url: URL(string: currentSong.imageURL ?? "")) { state in
                                        if let image = state.image {
                                            image
                                                .resizable()
                                                .aspectRatio(contentMode: .fill)
                                        }
                                    }

                                    RoundedRectangle(cornerRadius: 8)
                                        .foregroundStyle(.black)
                                        .opacity(audioManager.showBufferingIndicator ? 0.5 : 0)
                                        .animation(.easeInOut(duration: 0.2), value: audioManager.showBufferingIndicator)
                                }
                            }
                            .clipShape(RoundedRectangle(cornerRadius: 8))

                        if audioManager.showBufferingIndicator {
                            ProgressView()
                                .tint(.white)
                                .transition(.opacity)
                        }
                    }
                    .frame(width: 34, height: 34)
                    .padding(.leading, 16)

                    VStack(alignment: .leading) {
                        Text(currentSong.name)
                            .font(Constants.Typography.smallTitle)
                        Text(currentSong.artistName)
                            .font(Constants.Typography.xSmallTitle)
                    }
                    Spacer()
                    
                    if audioManager.playbackContext == .hooks {
                        Button {
                            print("not implemented")
                        } label: {
                            Image(systemName: "plus")
                                .fontWeight(.semibold)
                        }
                        .tint(Constants.Colors.Foreground.primary)
                        .padding(.trailing, 16)
                    } else {
                        Button {
                            audioManager.togglePlayback(song: currentSong)
                        } label: {
                            Image(systemName: audioManager.isCurrentlyPlaying ? "pause.fill" : "play.fill")
                        }
                        .tint(Constants.Colors.Foreground.primary)
                        .padding(.trailing, 8)
                        Button {
                            audioManager.nextSong()
                        } label: {
                            Image(systemName: "forward.fill")
                        }
                        .tint(Constants.Colors.Foreground.primary)
                        .padding(.trailing, 16)
                    }
                }
                .contentShape(.rect)
                .onTapGesture {
                    print("ontapgesture")
                    appState.showOmniplayer = true
                }
            }
        }
        .onAppear {
            print("tab accessory onAppear")
//            appState.isTabBottomAccessoryExpanded = (placement == .expanded)
        }
        .onChange(of: placement) { _, newValue in
            print("tab accessory onChange")
            withAnimation(.easeInOut(duration: 0.1)) {
                tabBarState.isTabBottomAccessoryExpanded = (newValue == .expanded)
            }
        }
    }
}

#Preview {
    ContentView()
}
