import ComponentLibrary
import ComposableArchitecture
import NukeUI
import SunoModelClient
import SwiftUI
import TabBarUtilities
import Utilities

public struct HooksTabBar: View {
    @Bindable var store: StoreOf<RootTabCoordinator>
    @Shared(.inMemory(.isOnHooksFeed)) var isOnHooksFeed: Bool = false
    @Shared(.inMemory(.overrideColorScheme)) var overrideColorScheme: ColorScheme? = nil

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

    private var tabBarAccent: Color {
        Color.SemanticV2.foregroundPrimary
    }

    // Dynamic color scheme that forces dark mode when we're on the Hooks tab,
    // otherwise uses the system color or the overridden color in window.overrideUserInterfaceStyle
    // from `AppearanceScreen.swift`
    private var tabBarAccentColorScheme: ColorScheme {
        if let overrideColorScheme {
            return overrideColorScheme
        }
        
        if isOnHooksFeed {
            return .dark
        } else {
            let defaultColorScheme = UIScreen.main.traitCollection.userInterfaceStyle == .dark ? ColorScheme.dark : ColorScheme.light
            guard let window = UIWindow.current,
                  window.overrideUserInterfaceStyle != .unspecified
            else {
                return defaultColorScheme
            }
            return window.overrideUserInterfaceStyle == .dark ? .dark : defaultColorScheme
        }
    }

    private var tabBarCreateBackground: some View {
        #if targetEnvironment(simulator)
            Color.orange
        #else
            AuraShaderView(
                id: "welcome-screen",
                appPreset: .pinkYellowOrange,
                morphSpeed: 0.025,
                scale: 0.6,
                seed: 0
            )
        #endif
    }

    private var hideTabBar: Bool {
        // Hide tab bar icons when HooksContextualFeed is active
        if store.isHooksContextualFeedActive {
            return true
        }

        return false
    }

    private var isReloadingHooksFeed: Bool {
        // When the user is on the Hooks tab root and viewing the Hooks feed,
        // then taps the Hooks tab icon to reload the feed,
        // we show a reload icon that spins until the reloaded content is loaded
        let isOnHooksTabFeed = store.selectedTab == .hooks && store.hooksStack.path.ids.isEmpty
        let hooksFeedLoadingState = store.hooks?.hooksFeed.loadingState
        let isReloading = hooksFeedLoadingState == .reloading
        return isOnHooksTabFeed && isReloading
    }

    // Inverse
    private var invertedRoundedCornerShape: some View {
        Rectangle()
            .fill(Color.SemanticV1.backgroundPrimary)
            .frame(height: 30)
            .mask(alignment: .bottom) {
                Rectangle()
                    .overlay(alignment: .bottom) {
                        UnevenRoundedRectangle(
                            topLeadingRadius: 0,
                            bottomLeadingRadius: 30,
                            bottomTrailingRadius: 30,
                            topTrailingRadius: 0
                        )
                        .blendMode(.destinationOut)
                    }
            }
            .opacity(store.shouldShowMaskedTabBar ? 1 : 0)
            .environment(\.colorScheme, .dark)
    }

    public let tabBarHeight: CGFloat = CustomBottomBarConstants.tabBarHeight
    public let tabBarTopPadding: CGFloat = CustomBottomBarConstants.tabBarTopPadding
    public let tabBarTopHairlineHeight: CGFloat = CustomBottomBarConstants.tabBarTopHairlineHeight

    @ViewBuilder
    private var orderedTabViews: some View {
        ForEach(store.orderedTabs, id: \.self) { tab in
            switch tab {
            case .hooks:
                hooksTabButton
            case .discover:
                exploreTabButton
            case .library:
                libraryTabButton
            case .notifications:
                notificationsTabButton
            case .profile:
                profileTabButton
            }
            
            if tab == store.orderedTabs.dropFirst().first {
                createButton
            }
        }
    }

    public var body: some View {
        VStack(spacing: 0) {
            // Shows on the Hooks tab to make scrolling feel more
            // continuous since we add rounded corners to each video cell
            invertedRoundedCornerShape
                .opacity(hideTabBar ? 0 : 1)

            Color.clear
                .overlay {
                    HStack(alignment: .center, spacing: 0) {
                        orderedTabViews
                    }
                    .padding(.top, tabBarTopPadding)
                    .frame(height: tabBarHeight)
                    .offset(y: hideTabBar ? tabBarHeight : 0)
                    .opacity(hideTabBar ? 0 : 1)
                }
                .frame(height: tabBarHeight)
                .background {
                    Color.SemanticV1.backgroundPrimary
                        .ignoresSafeArea(edges: .bottom)
                        .opacity(hideTabBar ? 0 : 1)
                        .overlay(alignment: .top) {
                            Rectangle()
                                .frame(height: tabBarTopHairlineHeight)
                                .foregroundColor(Color.SemanticV1.tabBarHairline)
                                .opacity(store.shouldShowMaskedTabBar || hideTabBar ? 0 : 1)
                        }
                }
                .accentColor(tabBarAccent)
                .animation(.spring(duration: 0.25), value: overrideColorScheme)
                .animation(.spring(duration: 0.25), value: hideTabBar)
                .environment(\.colorScheme, tabBarAccentColorScheme)
        }
    }

    // Used on the Discover, Library, and Hooks tabs
    private func tabButton(
        icon: Image,
        isSelected: Bool,
        tab: TabBarTab,
        accentColor: Color,
        isReloading: Bool = false
    ) -> some View {
        TabBarButton(
            icon: { icon },
            isSelected: isSelected,
            tab: tab,
            accentColor: accentColor,
            isReloading: isReloading,
            singleTapAction: {
                store.send(.popToRoot(tab: tab))
            },
            doubleTapAction: {
                store.send(.scrollToTop(tab: tab))
            },
            longPressAction: {
                store.send(.longPressAction(tab: tab))
            },
            didSelectTab: {
                store.send(.tabSelected(tab))
            }
        )
    }

    @ViewBuilder
    private var hooksTabButton: some View {
        tabButton(
            icon: Image.Icon.hooksTabIcon,
            isSelected: store.selectedTab == .hooks,
            tab: .hooks,
            accentColor: tabBarAccent,
            isReloading: isReloadingHooksFeed
        )
    }

    private var exploreTabButtonIcon: Image {
        if store.orderedTabs.first == TabBarTab.hooks {
            Image.Icon.exploreTabIcon
        } else {
            Image.Icon.homeTabIconSelected // Inactive color when not selected
        }
    }

    @ViewBuilder
    private var exploreTabButton: some View {
        tabButton(
            icon: exploreTabButtonIcon,
            isSelected: store.selectedTab == .discover,
            tab: .discover,
            accentColor: tabBarAccent
        )
    }

    @ViewBuilder
    private var profileTabButton: some View {
        if let profileImageUrl = store.me.user.avatarImageUrl.map(URL.init(string:)) {
            TabBarButton(
                icon: {
                    NukeUI.LazyImage(url: profileImageUrl) {
                        ($0.image ?? Image(systemName: "circle.fill"))
                            .resizable()
                            .frame(width: 22, height: 22)
                            .clipShape(Circle())
                            .foregroundStyle(Color.SemanticV1.iconSecondary)
                            .overlay {
                                if store.selectedTab == .profile {
                                    Circle()
                                        .strokeBorder(Color.SemanticV1.iconLink, lineWidth: 1.6)
                                        .frame(width: 28, height: 28)
                                }
                            }
                    }
                },
                isSelected: store.selectedTab == .profile,
                tab: .profile,
                accentColor: tabBarAccent,
                singleTapAction: {
                    store.send(.popToRoot(tab: .profile))
                },
                doubleTapAction: {
                    store.send(.scrollToTop(tab: .profile))
                },
                longPressAction: {
                    store.send(.longPressAction(tab: .profile))
                },
                didSelectTab: {
                    store.send(.tabSelected(.profile))
                }
            )
            .padding(.bottom, 1) // Visual offset to align when the tab is selected
        }
    }

    // Used on the Notifications tab
    private func badgedTabButton(
        image: UIImage,
        isSelected: Bool,
        showBadge: Bool,
        tab: TabBarTab,
        accentColor: Color
    ) -> some View {
        TabBarButton(
            icon: {
                ZStack {
                    Image(uiImage: image)
                        .renderingMode(.template)
                        .foregroundColor(isSelected ? accentColor : Color.SemanticV1.iconSecondary)

                    if showBadge {
                        Circle()
                            .fill(Color.SemanticV1.auraPink)
                            .frame(width: 6, height: 6)
                            .offset(x: 12, y: -12)
                    }
                }
            },
            isSelected: isSelected,
            tab: tab,
            accentColor: accentColor,
            singleTapAction: {
                store.send(.popToRoot(tab: tab))
            },
            doubleTapAction: {
                store.send(.scrollToTop(tab: tab))
            },
            longPressAction: {
                store.send(.longPressAction(tab: tab))
            },
            didSelectTab: {
                store.send(.tabSelected(tab))
            }
        )
    }

    // We should never see this in HooksTabBar, but it's just in case
    // one of the FeatureFlags above (first vs second tab) isn't set
    // for some reason
    @ViewBuilder
    private var notificationsTabButton: some View {
        badgedTabButton(
            image: store.selectedTab == .notifications ? UIImage.Icon.notificationsTabIconSelected
                .withRenderingMode(.alwaysTemplate) : UIImage.Icon.notificationsTabIcon
                .withRenderingMode(.alwaysTemplate),
            isSelected: store.selectedTab == .notifications,
            showBadge: store.hasUnreadNotifications,
            tab: .notifications,
            accentColor: tabBarAccent
        )
    }

    // Create button
    @ViewBuilder
    private var createButton: some View {
        if #available(iOS 26.0, *) {
            createButtonContent
                .buttonStyle(CleanButtonStyle())
                .glassEffect(.regular.interactive(), in: .capsule)
                .padding(.bottom, UIScreen.isCompact ? 2 : 0)
                .padding(.horizontal, 6)
        } else {
            createButtonContent
                .buttonStyle(ScaleButtonStyle(scaleAmount: 0.98, minimumOpacity: 1))
                .padding(.bottom, UIScreen.isCompact ? 2 : 0)
                .padding(.horizontal, 6)
        }
    }
    
    // Create button
    @State private var createButtonHapticProxy = false
    
    private var createButtonContent: some View {
        Button {
            createButtonHapticProxy.toggle()
            store.send(.createTapped)
        } label: {
            ZStack {
                tabBarCreateBackground
                Image.Icon.createTabIcon
                    .foregroundColor(.white)
            }
            .clipShape(Capsule())
            .frame(width: 64, height: 40)
        }
        .sensoryFeedbackIfEnabled(.impact(weight: .medium), trigger: createButtonHapticProxy)
    }

    // Library button with animated new gens badge count
    @ViewBuilder
    private var libraryTabButton: some View {
        tabButton(
            icon: Image.Icon.libraryTabIcon,
            isSelected: store.selectedTab == .library,
            tab: .library,
            accentColor: tabBarAccent
        )
        .overlay {
            if store.showNewGensBadge {
                newGensBadge
                    .transition(.scale(scale: 0.8).combined(with: .opacity))
            }
        }
        .animation(.bouncy(duration: 0.5), value: store.showNewGensBadge)
    }

    @State private var newGensBadgeDidAppear: Bool = false
    @Namespace private var newGensBadgeNamespace
    private let newGensBadgeLargeDuration: TimeInterval = 2.0

    @ViewBuilder
    private var newGensBadge: some View {
        Text("\(store.readyNewGensCount)")
            .contentTransition(.numericText())
            .typographyV1(.body1.size { _ in newGensBadgeDidAppear ? 12.0 : 16.0 })
            .foregroundColor(.white)
            .padding(.vertical, newGensBadgeDidAppear ? 2.0 : 5.0)
            .padding(.horizontal, newGensBadgeDidAppear ? 8.0 : 13.0)
            .animation(.easeInOut(duration: 0.2), value: newGensBadgeDidAppear)
            .background {
                Capsule()
                    .fill(Color.SemanticV1.auraPink)
                    .stroke(Color.SemanticV1.backgroundPrimary, lineWidth: 2.0)
            }
            .offset(x: newGensBadgeDidAppear ? 8 : 0, y: newGensBadgeDidAppear ? -9 : 0)
            .matchedGeometryEffect(id: "newGensBadge", in: newGensBadgeNamespace)
            .animation(.bouncy(duration: 0.5, extraBounce: 0.1), value: newGensBadgeDidAppear)
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + newGensBadgeLargeDuration) {
                    withAnimation {
                        newGensBadgeDidAppear = true
                    }
                }
            }
            .onChange(of: store.readyNewGensCount) { oldValue, newValue in
                guard newValue > oldValue, newValue > 0, oldValue > 0 else { return }
                // Animate the badge when the count changes
                withAnimation {
                    newGensBadgeDidAppear = false
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + newGensBadgeLargeDuration) {
                    withAnimation {
                        newGensBadgeDidAppear = true
                    }
                }
            }
    }
}
