import SwiftUI
import CoreData

// MARK: - App Tab

enum AppTab: Int, Hashable, CaseIterable {
    case dashboard = 0, sources = 1, charts = 2, journal = 3, settings = 4

    var title: String {
        switch self {
        case .dashboard: String(localized: "tab_dashboard")
        case .sources:   String(localized: "tab_sources")
        case .charts:    String(localized: "tab_charts")
        case .journal:   "Journal"
        case .settings:  String(localized: "tab_settings")
        }
    }

    var icon: String {
        switch self {
        case .dashboard: "house.fill"
        case .sources:   "list.bullet"
        case .charts:    "chart.xyaxis.line"
        case .journal:   "book.closed"
        case .settings:  "gearshape.fill"
        }
    }
}

// MARK: - Content View

struct ContentView: View {
    @EnvironmentObject var iapService: IAPService
    @EnvironmentObject var adMobService: AdMobService
    @EnvironmentObject var tabSelection: TabSelectionStore
    @EnvironmentObject var coreDataStack: CoreDataStack
    @AppStorage("onboardingCompleted") private var onboardingCompleted = false
    @AppStorage("faceIdEnabled") private var faceIdEnabled = false
    @AppStorage("pinEnabled") private var pinEnabled = false
    @AppStorage("lockOnLaunch") private var lockOnLaunch = true
    @AppStorage("lockOnBackground") private var lockOnBackground = false
    @AppStorage("lastSeenWhatsNewVersion") private var lastSeenWhatsNewVersion = ""
    @Environment(\.scenePhase) private var scenePhase
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @State private var isUnlocked = false
    @State private var resolvedOnboardingCompleted: Bool?
    @State private var iCloudCheckDone = false
    @State private var loadingMessageKey: LocalizedStringKey = "loading_data"
    @State private var sidebarSelection: AppTab? = .dashboard
    @State private var showingWhatsNew = false

    private var currentVersion: String {
        Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
    }

    private var lockEnabled: Bool {
        faceIdEnabled || pinEnabled
    }

    /// True when a fresh install with iCloud available and sync not yet enabled.
    /// Only relevant before onboarding is completed.
    private var needsICloudCheck: Bool {
        guard resolvedOnboardingCompleted == false else { return false }
        guard !UserDefaults.standard.bool(forKey: "cloudSyncEnabled") else { return false }
        return FileManager.default.ubiquityIdentityToken != nil
    }

    var body: some View {
        ZStack {
            Group {
                if !isReadyForContent {
                    AppLaunchLoadingView(messageKey: loadingMessageKey)
                } else if needsICloudCheck && !iCloudCheckDone {
                    // Fresh install with iCloud available: ask before showing onboarding
                    OnboardingICloudCheckView(onSkip: { iCloudCheckDone = true })
                } else if resolvedOnboardingCompleted == false {
                    OnboardingView(onboardingCompleted: $onboardingCompleted)
                } else if horizontalSizeClass == .regular {
                    iPadMainContent
                } else {
                    mainContent
                }
            }

            if onboardingCompleted && lockEnabled && !isUnlocked {
                AppLockView(isUnlocked: $isUnlocked)
            }
        }
        .onAppear {
            if !lockEnabled {
                isUnlocked = true
            } else {
                isUnlocked = !lockOnLaunch
            }
        }
        .onChange(of: lockEnabled) { _, enabled in
            if !enabled {
                isUnlocked = true
            } else {
                isUnlocked = !lockOnLaunch
            }
        }
        .onChange(of: onboardingCompleted) { _, completed in
            if completed && lockEnabled {
                isUnlocked = !lockOnLaunch
            }
        }
        .onChange(of: scenePhase) { _, phase in
            guard onboardingCompleted, lockEnabled else { return }
            if phase == .background && lockOnBackground {
                isUnlocked = false
            }
        }
        .task {
            await waitForDataAndResolveOnboarding()
            if (resolvedOnboardingCompleted == true) && currentVersion != lastSeenWhatsNewVersion {
                // Small delay to let the UI settle before showing sheet
                try? await Task.sleep(nanoseconds: 300_000_000)
                showingWhatsNew = true
            }
        }
        .onChange(of: onboardingCompleted) { _, completed in
            resolvedOnboardingCompleted = completed
        }
        .sheet(isPresented: $showingWhatsNew, onDismiss: {
            lastSeenWhatsNewVersion = currentVersion
        }) {
            WhatsNewView()
        }
        .onReceive(NotificationCenter.default.publisher(for: .openBatchUpdate)) { _ in
            tabSelection.selectedTab = 1
            sidebarSelection = .sources
            NotificationCenter.default.post(name: .openQuickUpdate, object: nil)
        }
        .onReceive(NotificationCenter.default.publisher(for: .openDashboard)) { _ in
            tabSelection.selectedTab = 0
            sidebarSelection = .dashboard
        }
        .onOpenURL { url in
            if url.host == "quickupdate" {
                tabSelection.selectedTab = 0
                sidebarSelection = .dashboard
                NotificationCenter.default.post(name: .openQuickUpdate, object: nil)
            }
        }
    }

    private var isReadyForContent: Bool {
        coreDataStack.isLoaded && resolvedOnboardingCompleted != nil
    }

    private func waitForDataAndResolveOnboarding() async {
        // Wait for Core Data to be loaded
        while !coreDataStack.isLoaded {
            try? await Task.sleep(nanoseconds: 50_000_000) // 50ms
        }

        // If CloudKit is enabled and no local data yet, wait briefly for the
        // initial iCloud sync so data from other devices appears before we
        // decide whether to show onboarding.
        if UserDefaults.standard.bool(forKey: "cloudSyncEnabled") && !hasExistingData() {
            await MainActor.run { loadingMessageKey = "checking_icloud" }
            await waitForInitialCloudKitSync(timeout: 10)
            await MainActor.run { loadingMessageKey = "loading_data" }
        }

        // Resolve onboarding state on main thread
        await MainActor.run {
            syncOnboardingState()
        }
    }

    /// Polls for existing data up to `timeout` seconds, returning as soon as
    /// any data appears. Used to wait for the initial CloudKit sync on launch.
    private func waitForInitialCloudKitSync(timeout: TimeInterval) async {
        let deadline = Date().addingTimeInterval(timeout)
        while Date() < deadline {
            if hasExistingData() { return }
            try? await Task.sleep(nanoseconds: 300_000_000) // poll every 300ms
        }
    }

    private func syncOnboardingState() {
        let settings = AppSettings.getOrCreate(in: coreDataStack.viewContext)
        var resolved = settings.onboardingCompleted || onboardingCompleted

        // If user has existing data, skip onboarding
        if !resolved && hasExistingData() {
            resolved = true
        }

        // Persist the resolved state
        if settings.onboardingCompleted != resolved {
            settings.onboardingCompleted = resolved
            CoreDataStack.shared.save()
        }
        if onboardingCompleted != resolved {
            onboardingCompleted = resolved
        }

        resolvedOnboardingCompleted = resolved
    }

    private func hasExistingData() -> Bool {
        let context = coreDataStack.viewContext

        let sourceRequest: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
        sourceRequest.fetchLimit = 1
        sourceRequest.resultType = .countResultType
        if (try? context.count(for: sourceRequest)) ?? 0 > 0 {
            return true
        }

        let snapshotRequest: NSFetchRequest<Snapshot> = Snapshot.fetchRequest()
        snapshotRequest.fetchLimit = 1
        snapshotRequest.resultType = .countResultType
        if (try? context.count(for: snapshotRequest)) ?? 0 > 0 {
            return true
        }

        let accountRequest: NSFetchRequest<Account> = Account.fetchRequest()
        accountRequest.fetchLimit = 1
        accountRequest.resultType = .countResultType
        return ((try? context.count(for: accountRequest)) ?? 0) > 0
    }

    // MARK: - iPad Layout

    private var iPadMainContent: some View {
        NavigationSplitView(columnVisibility: .constant(.all)) {
            List(AppTab.allCases, id: \.self, selection: $sidebarSelection) { tab in
                Label(tab.title, systemImage: tab.icon)
            }
            .navigationTitle("Portfolio Journal")
            .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 280)
        } detail: {
            iPadDetailView(for: sidebarSelection ?? .dashboard)
        }
        .onChange(of: sidebarSelection) { _, tab in
            guard let tab else { return }
            if tabSelection.selectedTab != tab.rawValue {
                tabSelection.selectedTab = tab.rawValue
            }
        }
        .onChange(of: tabSelection.selectedTab) { _, value in
            if let tab = AppTab(rawValue: value), sidebarSelection != tab {
                sidebarSelection = tab
            }
        }
    }

    @ViewBuilder
    private func iPadDetailView(for tab: AppTab) -> some View {
        switch tab {
        case .dashboard:
            bannerInsetView(DashboardView())
        case .sources:
            bannerInsetView(SourceListView(iapService: iapService))
        case .charts:
            bannerInsetView(ChartsContainerView(iapService: iapService))
        case .journal:
            bannerInsetView(JournalView())
        case .settings:
            bannerInsetView(SettingsView(iapService: iapService))
        }
    }

    // MARK: - iPhone Layout

    private var mainContent: some View {
        ZStack {
            TabView(selection: $tabSelection.selectedTab) {
                bannerInsetView(DashboardView())
                    .tabItem {
                        Label("Home", systemImage: "house.fill")
                    }
                    .tag(0)

                bannerInsetView(SourceListView(iapService: iapService))
                    .tabItem {
                        Label("Sources", systemImage: "list.bullet")
                    }
                    .tag(1)

                bannerInsetView(ChartsContainerView(iapService: iapService))
                    .tabItem {
                        Label("Charts", systemImage: "chart.xyaxis.line")
                    }
                    .tag(2)

                bannerInsetView(JournalView())
                    .tabItem {
                        Label("Journal", systemImage: "book.closed")
                    }
                    .tag(3)

                bannerInsetView(SettingsView(iapService: iapService))
                    .tabItem {
                        Label("Settings", systemImage: "gearshape.fill")
                    }
                    .tag(4)
            }
        }
    }

    private func bannerInsetView<Content: View>(_ content: Content) -> some View {
        content.safeAreaInset(edge: .bottom, spacing: 0) {
            if !iapService.isPremium && adMobService.canShowAds {
                BannerAdView()
                    .frame(height: AppConstants.UI.bannerAdHeight)
                    .frame(maxWidth: .infinity)
                    .background(Color(.systemBackground))
            }
        }
    }
}

#Preview {
    ContentView()
        .environmentObject(CoreDataStack.shared)
        .environmentObject(IAPService())
        .environmentObject(AdMobService())
        .environmentObject(AccountStore(iapService: IAPService()))
        .environmentObject(TabSelectionStore())
}
