import SwiftUI

/// Main container view with tab-based navigation.
struct MainTabView: View {
    var onLogout: (() -> Void)?
    /// Always start on Chats tab after login / account switch.
    /// Using @State (not @SceneStorage) ensures the tab resets to .chats
    /// when MainTabView is recreated after an account switch or unlock.
    @State private var selectedTab: RosettaTab = .chats
    @State private var isChatSearchActive = false
    @State private var isChatListDetailPresented = false
    @State private var isSettingsEditPresented = false
    @State private var isSettingsDetailPresented = false
    @StateObject private var callManager = CallManager.shared

    // Add Account — presented as fullScreenCover so Settings stays alive.
    // Using optional AuthScreen as the item ensures the correct screen is
    // passed directly to the content closure (no stale capture).
    @State private var addAccountScreen: AuthScreen?
    /// All tabs are pre-activated so that switching only changes the offset,
    /// not the view structure. Creating a NavigationStack mid-animation causes
    /// "Update NavigationRequestObserver tried to update multiple times per frame" → freeze.
    @State private var activatedTabs: Set<RosettaTab> = Set(RosettaTab.interactionOrder)
    /// When non-nil, the tab bar is being dragged and the pager follows interactively.
    @State private var dragFractionalIndex: CGFloat?

    /// Local handler for Add Account — triggers fullScreenCover instead of app-level navigation.
    private var handleAddAccount: (AuthScreen) -> Void {
        { screen in
            addAccountScreen = screen
        }
    }

    var body: some View {
        let _ = PerformanceLogger.shared.track("mainTab.bodyEval")
        ZStack {
            Group {
                if #available(iOS 26.0, *) {
                    systemTabView
                } else {
                    legacyTabView
                }
            }
            .fullScreenCover(item: $addAccountScreen) { screen in
                AuthCoordinator(
                    onAuthComplete: {
                        addAccountScreen = nil
                        // New account created — end session and go to unlock for the new account
                        SessionManager.shared.endSession()
                        onLogout?()
                    },
                    onBackToUnlock: {
                        addAccountScreen = nil
                    },
                    initialScreen: screen
                )
            }

            // Full-screen device verification overlay (observation-isolated).
            // Covers nav bar, search bar, and tab bar — desktop parity.
            DeviceConfirmOverlay()

            // PERF: observation-isolated unread counter.
            // Reads DialogRepository in its own scope — MainTabView.body
            // never observes the dialogs dictionary directly.
            UnreadCountObserver(count: $cachedUnreadCount)

            if callManager.uiState.isVisible {
                ActiveCallOverlayView(callManager: callManager)
                    .zIndex(10)
            }
        }
        // Switch to Chats tab when user taps a push notification.
        // Without this, the navigation happens in the Chats NavigationStack
        // but the user stays on whatever tab they were on (e.g., Settings).
        .onReceive(NotificationCenter.default.publisher(for: .openChatFromNotification)) { _ in
            if selectedTab != .chats {
                selectedTab = .chats
            }
        }
    }

    // MARK: - iOS 26+ (native TabView with liquid glass tab bar)

    @available(iOS 26.0, *)
    private var systemTabView: some View {
        TabView(selection: $selectedTab) {
            CallsView()
                .tabItem {
                    Label(RosettaTab.calls.label, systemImage: RosettaTab.calls.icon)
                }
                .tag(RosettaTab.calls)

            ChatListView(
                isSearchActive: $isChatSearchActive,
                isDetailPresented: $isChatListDetailPresented
            )
                .tabItem {
                    Label(RosettaTab.chats.label, systemImage: RosettaTab.chats.icon)
                }
                .tag(RosettaTab.chats)
                .badge(cachedUnreadCount)

            SettingsView(onLogout: onLogout, onAddAccount: handleAddAccount, isEditingProfile: $isSettingsEditPresented, isDetailPresented: $isSettingsDetailPresented)
                .tabItem {
                    Label(RosettaTab.settings.label, systemImage: RosettaTab.settings.icon)
                }
                .tag(RosettaTab.settings)
        }
        .tint(RosettaColors.primaryBlue)
    }

    // MARK: - iOS < 26 (custom RosettaTabBar with pager)

    private var legacyTabView: some View {
        ZStack(alignment: .bottom) {
            RosettaColors.Adaptive.background
                .ignoresSafeArea()

            GeometryReader { geometry in
                tabPager(availableSize: geometry.size)
            }
            .ignoresSafeArea()

            if !isChatSearchActive && !isAnyChatDetailPresented && !isSettingsEditPresented && !isSettingsDetailPresented {
                RosettaTabBar(
                    selectedTab: selectedTab,
                    onTabSelected: { tab in
                        selectedTab = tab
                    },
                    onSwipeStateChanged: { state in
                        if let state {
                            for tab in RosettaTab.interactionOrder {
                                activatedTabs.insert(tab)
                            }
                            dragFractionalIndex = state.fractionalIndex
                        } else {
                            withAnimation(.easeInOut(duration: 0.15)) {
                                dragFractionalIndex = nil
                            }
                        }
                    }
                )
                .ignoresSafeArea(.keyboard)
                .transition(.move(edge: .bottom).combined(with: .opacity))
            }
        }
        .ignoresSafeArea(.keyboard)
        .onChange(of: isChatSearchActive) { _, isActive in
            if isActive {
                dragFractionalIndex = nil
            }
        }
    }

    private var currentPageIndex: CGFloat {
        CGFloat(selectedTab.interactionIndex)
    }

    @ViewBuilder
    private func tabPager(availableSize: CGSize) -> some View {
        let width = max(1, availableSize.width)

        ZStack {
            ForEach(RosettaTab.interactionOrder, id: \.self) { tab in
                tabView(for: tab)
                    .frame(width: width, height: availableSize.height)
                    .opacity(tabOpacity(for: tab))
                    .environment(\.telegramGlassActive, tabOpacity(for: tab) > 0)
                    .animation(.easeOut(duration: 0.12), value: selectedTab)
                    .allowsHitTesting(tab == selectedTab && dragFractionalIndex == nil)
            }
        }
        .clipped()
    }

    private func tabOpacity(for tab: RosettaTab) -> Double {
        if let frac = dragFractionalIndex {
            // During drag: crossfade between adjacent tabs
            let tabIndex = CGFloat(tab.interactionIndex)
            let distance = abs(frac - tabIndex)
            if distance >= 1 { return 0 }
            return Double(1 - distance)
        } else {
            return tab == selectedTab ? 1 : 0
        }
    }

    @ViewBuilder
    private func tabView(for tab: RosettaTab) -> some View {
        if activatedTabs.contains(tab) {
            switch tab {
            case .chats:
                ChatListView(
                    isSearchActive: $isChatSearchActive,
                    isDetailPresented: $isChatListDetailPresented
                )
            case .calls:
                CallsView()
            case .settings:
                SettingsView(onLogout: onLogout, onAddAccount: handleAddAccount, isEditingProfile: $isSettingsEditPresented, isDetailPresented: $isSettingsDetailPresented)
            }
        } else {
            RosettaColors.Adaptive.background
        }
    }

    private var isAnyChatDetailPresented: Bool {
        isChatListDetailPresented
    }

    private var tabBadges: [TabBadge] {
        guard let chatUnreadBadge else {
            return []
        }
        return [TabBadge(tab: .chats, text: chatUnreadBadge)]
    }

    /// PERF: cached unread count — updated by observation-isolated child view.
    /// Reading DialogRepository.shared.sortedDialogs directly in MainTabView.body
    /// creates observation on the entire dialogs dictionary, causing full body
    /// re-evaluation on every dialog mutation (online, typing, delivery).
    @State private var cachedUnreadCount: Int = 0

    private var chatUnreadBadge: String? {
        if cachedUnreadCount <= 0 {
            return nil
        }
        return cachedUnreadCount > 999 ? "\(cachedUnreadCount / 1000)K" : "\(cachedUnreadCount)"
    }
}

// MARK: - Observation-Isolated Unread Counter

/// Invisible child view that reads DialogRepository in its OWN observation scope.
/// MainTabView.body never observes DialogRepository directly — only this tiny view
/// re-evaluates when dialogs change, updating the parent via @Binding.
private struct UnreadCountObserver: View {
    @Binding var count: Int

    private var unreadTotal: Int {
        DialogRepository.shared.sortedDialogs
            .reduce(0) { $0 + ($1.isMuted ? 0 : $1.unreadCount) }
    }

    var body: some View {
        let total = unreadTotal
        Color.clear
            .frame(width: 0, height: 0)
            .allowsHitTesting(false)
            .task(id: total) { count = total }
    }
}

private extension View {
    /// Non-conditional badge — preserves structural identity.
    /// A @ViewBuilder `if let / else` creates two branches; switching
    /// between them changes the child's structural identity, destroying
    /// any @StateObject (including ChatListNavigationState.path).
    func badgeIfNeeded(_ value: String?) -> some View {
        badge(Text(value ?? ""))
    }
}

// MARK: - Pager Offset Modifier

/// Isolates the offset/animation from child view identity so that
/// changing `effectiveIndex` only redraws the transform, not the child views.
private struct PagerOffsetModifier: ViewModifier {
    let effectiveIndex: CGFloat
    let pageWidth: CGFloat
    let isDragging: Bool

    func body(content: Content) -> some View {
        content
            .offset(x: -effectiveIndex * pageWidth)
            .animation(
                isDragging ? nil : .spring(response: 0.34, dampingFraction: 0.82),
                value: effectiveIndex
            )
    }
}

// MARK: - Placeholder

struct PlaceholderTabView: View {
    let title: String
    let icon: String

    var body: some View {
        NavigationStack {
            ZStack {
                RosettaColors.Adaptive.background
                    .ignoresSafeArea()

                VStack(spacing: 16) {
                    Image(systemName: icon)
                        .font(.system(size: 52))
                        .foregroundStyle(RosettaColors.Adaptive.textSecondary.opacity(0.5))

                    Text(title)
                        .font(.system(size: 17, weight: .semibold))
                        .foregroundStyle(RosettaColors.Adaptive.textSecondary)

                    Text("Coming soon")
                        .font(.system(size: 15))
                        .foregroundStyle(RosettaColors.Adaptive.textTertiary)
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text(title)
                        .font(.system(size: 17, weight: .semibold))
                        .foregroundStyle(RosettaColors.Adaptive.text)
                }
            }
            .applyGlassNavBar()
        }
    }
}

// MARK: - Device Confirm Overlay (observation-isolated)

/// Reads `ProtocolManager` in its own observation scope.
/// Shows full-screen `DeviceConfirmView` when this device awaits approval.
private struct DeviceConfirmOverlay: View {
    var body: some View {
        if ProtocolManager.shared.connectionState == .deviceVerificationRequired {
            DeviceConfirmView()
                .transition(.opacity)
        }
    }
}
