import SwiftUI
import SDWebImageSwiftUI
import SDWebImageSVGCoder
import StoreKit
import UIKit
import AlertToast

/// Top-level destinations the app can route to based on connection + player state.
/// Pure value type so the routing decision is explicit and unit-testable, instead
/// of being buried in nested `if` conditions inside the View body.
enum AppRootDestination: Equatable {
    /// First-run onboarding flow (includes character creation).
    case onboarding
    /// Returning entry screen that ALSO offers character creation
    /// ("Begin Your Journey" -> CharacterSetupView).
    case welcome
    /// Character is alive and the game is playable.
    case game
    /// Character has died.
    case death
}

/// Pure mapping from persisted onboarding state + server status to a destination.
///
/// Guarantee: any `status == "creating"` ALWAYS routes to a destination that can
/// create a character (`.onboarding` or `.welcome`) regardless of whether
/// `onboardingComplete` was previously persisted. This prevents the stuck-at-gate
/// bug where a player who finished the tutorial once but never actually created a
/// character (status stayed "creating") had no path forward.
///
/// - Parameters:
///   - onboardingComplete: persisted `onboardingComplete` flag from UserDefaults.
///   - playerStatus: `webSocketService.player.status` ("creating" while no character).
///   - personStatus: `webSocketService.person.status` ("dead" after the character dies).
func resolveAppRootDestination(
    onboardingComplete: Bool,
    playerStatus: String,
    personStatus: String
) -> AppRootDestination {
    if playerStatus == "creating" {
        // Always provide a character-creation path. Fresh users (onboarding not
        // yet complete) get the full guided flow; everyone else gets the welcome
        // screen, which itself leads into CharacterSetupView.
        return onboardingComplete ? .welcome : .onboarding
    }
    if personStatus == "dead" {
        return .death
    }
    return .game
}

struct ContentView: View {
    @EnvironmentObject var webSocketService: WebSocketService
    @EnvironmentObject var appViewModel: AppViewModel
    @ObservedObject private var deepLinkManager = DeepLinkManager.shared
    @ObservedObject private var liveActivityManager = LiveActivityManager.shared
    // INTEGRATION: Toast notifications
    @EnvironmentObject var toastManager: ToastManager
    // INTEGRATION: Tooltips
    @EnvironmentObject var tooltipManager: TooltipManager

    // Branded cold-start splash ("Your life awaits"). Shown briefly on top of
    // everything, then fades into the connecting/home experience.
    @State private var showSplash = true

    var body: some View {
        ZStack {
            VStack {
                if webSocketService.isConnected {
                    // Route via the pure resolver so a "creating" status always
                    // reaches a character-creation path (see resolveAppRootDestination).
                    switch resolveAppRootDestination(
                        onboardingComplete: appViewModel.onboardingComplete,
                        playerStatus: webSocketService.player.status,
                        personStatus: webSocketService.person.status
                    ) {
                    case .onboarding:
                        // Full guided onboarding flow for new users
                        OnboardingContainerView()
                    case .welcome:
                        // Returning entry screen; "Begin Your Journey" leads to
                        // CharacterSetupView so a creating-status user can still
                        // create a character.
                        WelcomeView()
                    case .death:
                        DeathView()
                    case .game:
                        LiquidGlassTabContainer(
                            selectedTab: $appViewModel.selectedTab,
                            items: tabBarItems,
                            onMoreTapped: {
                                appViewModel.showQuickActions = true
                                hapticFeedback(style: .light)
                            }
                        ) {
                            VStack(spacing: 0) {
                                HeaderView()
                                    .environmentObject(appViewModel)

                                // Tab content
                                Group {
                                    switch appViewModel.selectedTab {
                                    case 0:
                                        HomeView()
                                    case 1:
                                        ActivityButtonsView()
                                    case 2:
                                        SocialView()
                                            .environmentObject(appViewModel)
                                    default:
                                        HomeView()
                                    }
                                }
                            }
                        }
                        .onAppear {
                            // Auto-start the game at moderate speed when the main view loads
                            webSocketService.sendMessage(message: WebSocketCommands.command("start"))
                            webSocketService.sendMessage(message: WebSocketCommands.speed(Constants.GameSpeed.normal))
                        }
                    }
                } else {
                    CozyConnectingView()
                        .onAppear(perform: webSocketService.connect)
                }

            }

            // POLISH (T014): floating "+5 energy" / "-3 money" deltas rise near
            // the header so resource changes feel earned. Renders only while a
            // playable session is on screen.
            if webSocketService.isConnected,
               webSocketService.player.status != "creating",
               webSocketService.person.status != "dead" {
                FloatingDeltaOverlay(gameState: webSocketService.gameState)
                    .padding(.top, AppSpacing.headerRowHeight)
                    .allowsHitTesting(false)
            }

            if let messageEvent = webSocketService.currentMessageEvent {
                EventModalView(questionID: messageEvent.id, question: messageEvent.message, answers: [], image: messageEvent.image) {
                    // Dismiss logic for the ModalView
                    webSocketService.currentMessageEvent = nil
                }
                .environmentObject(webSocketService)
                .background(AppColors.primaryBackground.edgesIgnoringSafeArea(.all)) // Adds a semi-transparent backdrop
            }
            // Overlaying the EventModalView on topion {
            if let question = webSocketService.currentQuestion {
                EventModalView(
                    questionID: question.id,
                    question: question.question,
                    answers: question.answers,
                    title: nil,
                    image: question.image,
                    characters: question.characters
                ) {
                    webSocketService.currentQuestion = nil
                }
                .environmentObject(webSocketService)
                .background(Color.black.opacity(1).edgesIgnoringSafeArea(.all)) // Adds a semi-transparent backdrop
            }

            // INTEGRATION: Relationship event modal
            if let relationshipEvent = webSocketService.currentRelationshipEvent {
                RelationshipEventModal(event: relationshipEvent) {
                    webSocketService.currentRelationshipEvent = nil
                }
                .environmentObject(webSocketService)
            }

            // Branded cold-start splash — sits above everything, then fades out.
            if showSplash {
                LaunchScreenView()
                    .transition(.opacity)
                    .zIndex(100)
                    .onAppear {
                        DispatchQueue.main.asyncAfter(deadline: .now() + 1.9) {
                            withAnimation(.easeOut(duration: 0.45)) {
                                showSplash = false
                            }
                        }
                    }
            }
        }
        .onChange(of: webSocketService.questionQueue, perform: { value in
            if !value.isEmpty {
                webSocketService.currentQuestion = value.first // Assumes you'd show the first question in the queue
            }
        })
        .onAppear {
            handlePendingDeepLink(deepLinkManager.pendingRoute)

            // Set up analytics user tracking when app loads
            if webSocketService.isConnected, !webSocketService.person.id.isEmpty {
                AnalyticsManager.shared.setUserID(webSocketService.person.id)

                // Set user properties
                if !webSocketService.person.firstname.isEmpty {
                    AnalyticsManager.shared.setUserProperty(
                        webSocketService.person.firstname,
                        forName: "character_name"
                    )
                }

                if webSocketService.person.ageYears > 0 {
                    AnalyticsManager.shared.setUserProperty(
                        "\(webSocketService.person.ageYears)",
                        forName: "character_age"
                    )
                }

                // RETENTION: Check daily rewards on connect
                webSocketService.fetchDailyRewards()
            }
        }
        .onChange(of: deepLinkManager.pendingRoute) { route in
            handlePendingDeepLink(route)
        }
        .task(id: liveActivityStateSignature) {
            await liveActivityManager.handlePlayableSession(webSocketService: webSocketService)
        }
        .alert("Follow your character live?", isPresented: $liveActivityManager.shouldShowPrompt) {
            Button("Follow Live") {
                Task {
                    await liveActivityManager.startFollowing(webSocketService: webSocketService)
                }
            }

            Button("Not Now", role: .cancel) {
                liveActivityManager.declinePrompt()
            }
        } message: {
            Text("Show current status, time, and core stats during this play session.")
        }
        // INTEGRATION: Toast overlay for notifications
        .toastOverlay()
        // INTEGRATION: Error recovery overlay
        .errorOverlay(
            error: $webSocketService.currentError,
            onRetry: {
                webSocketService.connect()
                AnalyticsManager.shared.track(.websocketError(errorType: "retry", message: "Manual retry"))
            }
        )
        // MONETIZATION: Energy refill modal
        .sheet(isPresented: $webSocketService.showEnergyRefillModal) {
            EnergyRefillModal()
                .environmentObject(webSocketService)
                .environmentObject(toastManager)
        }
        // MONETIZATION: Time skip modal
        .sheet(isPresented: $webSocketService.showTimeSkipModal) {
            TimeSkipModal()
                .environmentObject(webSocketService)
                .environmentObject(toastManager)
        }
        // MONETIZATION: Time skip summary modal
        .sheet(isPresented: $webSocketService.showTimeSkipSummary) {
            TimeSkipSummaryView()
                .environmentObject(webSocketService)
        }
        // RETENTION: Achievement unlock modal
        .sheet(isPresented: $webSocketService.showAchievementUnlock) {
            AchievementUnlockModal()
                .environmentObject(webSocketService)
        }
        // RETENTION: Daily rewards modal
        .sheet(isPresented: $webSocketService.showDailyRewards) {
            DailyRewardsView()
                .environmentObject(webSocketService)
        }
        // RETENTION: Daily quests modal
        .sheet(isPresented: $webSocketService.showDailyQuests) {
            DailyQuestsView()
                .environmentObject(webSocketService)
        }
        // NAVIGATION: Messages from header
        .sheet(isPresented: $appViewModel.showMessagesFromHeader) {
            MessagesView()
                .environmentObject(webSocketService)
                .environmentObject(appViewModel)
        }
        // NAVIGATION: Toast tap opens conversation directly
        .onChange(of: webSocketService.pendingOpenConversation) { characterId in
            if characterId != nil {
                // Check if any sheets are currently open
                let anySheetOpen = webSocketService.showEnergyRefillModal
                    || webSocketService.showTimeSkipModal
                    || webSocketService.showTimeSkipSummary
                    || webSocketService.showAchievementUnlock
                    || webSocketService.showDailyRewards
                    || webSocketService.showDailyQuests
                    || appViewModel.showProfileMenu
                    || appViewModel.showAchievements
                    || appViewModel.showRelationships
                    || appViewModel.showStore
                    || appViewModel.showItems
                    || appViewModel.showQuickActions

                // Dismiss any open sheets to avoid SwiftUI sheet collision
                webSocketService.showEnergyRefillModal = false
                webSocketService.showTimeSkipModal = false
                webSocketService.showTimeSkipSummary = false
                webSocketService.showAchievementUnlock = false
                webSocketService.showDailyRewards = false
                webSocketService.showDailyQuests = false
                appViewModel.showProfileMenu = false
                appViewModel.showAchievements = false
                appViewModel.showRelationships = false
                appViewModel.showStore = false
                appViewModel.showItems = false
                appViewModel.showQuickActions = false

                if anySheetOpen {
                    // Delay to let dismissed sheets animate out
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        appViewModel.showMessagesFromHeader = true
                    }
                } else {
                    // No sheets to dismiss, open immediately
                    appViewModel.showMessagesFromHeader = true
                }
            }
        }
        // NAVIGATION: Profile menu from header
        .sheet(isPresented: $appViewModel.showProfileMenu) {
            ProfileMenuView()
                .environmentObject(webSocketService)
                .environmentObject(appViewModel)
        }
        // NAVIGATION: Profile menu sub-views
        .sheet(isPresented: $appViewModel.showAchievements) {
            NavigationView {
                AchievementsView()
                    .environmentObject(webSocketService)
                    .environmentObject(appViewModel)
            }
            .navigationViewStyle(.stack)
        }
        .sheet(isPresented: $appViewModel.showRelationships) {
            NavigationView {
                RelationshipsView()
                    .environmentObject(webSocketService)
                    .environmentObject(appViewModel)
            }
            .navigationViewStyle(.stack)
        }
        .sheet(isPresented: $appViewModel.showStore) {
            NavigationView {
                StoreView()
                    .environmentObject(webSocketService)
                    .environmentObject(appViewModel)
            }
            .navigationViewStyle(.stack)
        }
        .sheet(isPresented: $appViewModel.showItems) {
            NavigationView {
                ItemsView()
                    .environmentObject(webSocketService)
                    .environmentObject(appViewModel)
            }
            .navigationViewStyle(.stack)
        }
        // PROGRESSION (Wave 2): perform-activity action sheet
        .sheet(isPresented: $appViewModel.showPerformActivity) {
            PerformActivityView()
                .environmentObject(webSocketService)
        }
        // PROGRESSION (Wave 2): life goals
        .sheet(isPresented: $appViewModel.showLifeGoals) {
            NavigationView {
                LifeGoalsView()
                    .environmentObject(webSocketService)
            }
            .navigationViewStyle(.stack)
        }
        // PROGRESSION (Wave 2): achievement collection
        .sheet(isPresented: $appViewModel.showCollection) {
            NavigationView {
                AchievementCollectionView()
                    .environmentObject(webSocketService)
            }
            .navigationViewStyle(.stack)
        }
        // PROGRESSION (Wave 2): prestige
        .sheet(isPresented: $appViewModel.showPrestige) {
            NavigationView {
                PrestigeView()
                    .environmentObject(webSocketService)
            }
            .navigationViewStyle(.stack)
        }
        // NAVIGATION: Quick actions menu
        .sheet(isPresented: $appViewModel.showQuickActions) {
            QuickActionsMenu(actions: quickActions)
        }
        // LIFECYCLE: One-time welcome-back digest after offline play
        .sheet(isPresented: offlineDigestPresented) {
            if let digest = webSocketService.pendingOfflineDigest {
                OfflineDigestView(digest: digest) {
                    webSocketService.pendingOfflineDigest = nil
                }
            }
        }

    }

    /// Presents the offline welcome-back digest once. Dismiss clears the pending
    /// digest so it never re-fires within the same session.
    private var offlineDigestPresented: Binding<Bool> {
        Binding(
            get: { webSocketService.pendingOfflineDigest != nil },
            set: { newValue in
                if !newValue { webSocketService.pendingOfflineDigest = nil }
            }
        )
    }

    // MARK: - Computed Properties

    private var tabBarItems: [TabBarItem] {
        [
            TabBarItem(id: 0, icon: "house", selectedIcon: "house.fill", label: "Home"),
            TabBarItem(id: 1, icon: "calendar", selectedIcon: "calendar", label: "Activities"),
            TabBarItem(
                id: 2,
                icon: "heart.circle",
                selectedIcon: "heart.circle.fill",
                label: "Social",
                badge: unreadMessagesCount
            ),
            TabBarItem(id: 3, icon: "line.3.horizontal.circle", selectedIcon: "line.3.horizontal.circle.fill", label: "More")
        ]
    }

    private var unreadMessagesCount: Int {
        webSocketService.player.activeConversations.filter { $0.unread }.count
    }

    private var liveActivityStateSignature: String {
        [
            webSocketService.isConnected ? "connected" : "disconnected",
            webSocketService.player.status,
            webSocketService.player.date,
            "\(webSocketService.player.hourOfDay)",
            "\(webSocketService.player.minuteOfHour)",
            webSocketService.player.season,
            "\(webSocketService.player.gameSpeed)",
            webSocketService.person.id,
            webSocketService.person.firstname,
            webSocketService.person.lastname,
            webSocketService.person.status,
            webSocketService.person.image,
            webSocketService.person.location,
            webSocketService.person.intraDayMessage,
            "\(webSocketService.person.ageYears)",
            "\(webSocketService.person.calcEnergy)",
            "\(webSocketService.person.health)",
            "\(webSocketService.person.happiness)",
        ].joined(separator: "|")
    }

    private var quickActions: [QuickAction] {
        [
            QuickAction(
                icon: "envelope.fill",
                title: "Messages",
                subtitle: unreadMessagesCount > 0 ? "\(unreadMessagesCount) unread" : "No new messages",
                badge: unreadMessagesCount,
                color: AppColors.primary
            ) {
                appViewModel.showMessagesFromHeader = true
            },
            QuickAction(
                icon: "trophy.fill",
                title: "Achievements",
                subtitle: webSocketService.unacknowledgedAchievements.isEmpty ? "Track your progress" : "\(webSocketService.unacknowledgedAchievements.count) new unlock",
                badge: webSocketService.unacknowledgedAchievements.count,
                color: AppColors.happiness
            ) {
                appViewModel.showAchievements = true
            },
            QuickAction(
                icon: "person.2.fill",
                title: "Relationships",
                subtitle: "Manage connections",
                color: AppColors.loveInterest
            ) {
                appViewModel.showRelationships = true
            },
            QuickAction(
                icon: "cart.fill",
                title: "Store",
                subtitle: "Shop for items",
                color: AppColors.money
            ) {
                appViewModel.showStore = true
            },
            QuickAction(
                icon: "bag.fill",
                title: "Items",
                subtitle: "Your inventory",
                color: AppColors.diamond
            ) {
                appViewModel.showItems = true
            },
            QuickAction(
                icon: "person.circle.fill",
                title: "Profile",
                subtitle: "Settings & more",
                color: AppColors.secondary
            ) {
                appViewModel.showProfileMenu = true
            }
        ]
    }

    private func handlePendingDeepLink(_ route: DeepLinkRoute?) {
        guard let route = route else { return }
        DeepLinkManager.shared.pendingRoute = nil
        DeepLinkManager.shared.handle(
            route: route,
            appViewModel: appViewModel,
            webSocketService: webSocketService
        )
    }
}

// MARK: - Launch Splash
//
// Cozy "sunrise — your life awaits" splash shown briefly on cold start (wired as
// an overlay above). Defined here (not a standalone file) because this project
// has no synchronized file groups — every source must be an explicit pbxproj
// member, so new structs live in already-compiled files. Echoes the Home
// header's seasonal world (warm dawn sky, friendly rising sun, soft hills,
// drifting petals) so the first frame already feels like BaoLife.

struct LaunchScreenView: View {
    @State private var appeared = false
    @State private var sunRise: CGFloat = 60      // sun starts low, rises
    @State private var glowPulse: CGFloat = 0.9
    @State private var shimmerOffset: CGFloat = -220
    @State private var petalDrift = false
    @State private var dotPhase = false

    private let cream = Color(hex: 0xFFF8F3)
    private let pink = Color(hex: 0xF4A5B5)
    private let pinkDeep = Color(hex: 0xE88CA0)
    private let gold = Color(hex: 0xFFD89B)
    private let sunCore = Color(hex: 0xFFE08A)
    private let textSoft = Color(hex: 0xB8A39D)

    var body: some View {
        GeometryReader { geo in
            let w = geo.size.width
            let h = geo.size.height

            ZStack {
                LinearGradient(
                    colors: [Color(hex: 0xFFE3C2), Color(hex: 0xFFEAD6), cream],
                    startPoint: .top, endPoint: .bottom
                )
                .ignoresSafeArea()

                Ellipse()
                    .fill(Color(hex: 0x9DDFAA).opacity(0.45))
                    .frame(width: w * 1.25, height: h * 0.34)
                    .position(x: w * 0.22, y: h * 0.92)
                Ellipse()
                    .fill(Color(hex: 0xB5C9F4).opacity(0.38))
                    .frame(width: w * 1.2, height: h * 0.32)
                    .position(x: w * 0.85, y: h * 0.96)

                ForEach(Array(petalSpots(w: w, h: h).enumerated()), id: \.offset) { _, p in
                    Text("🌸")
                        .font(.system(size: p.size))
                        .opacity(appeared ? p.opacity : 0)
                        .offset(y: petalDrift ? p.drift : -p.drift)
                        .position(x: p.x, y: p.y)
                        .animation(.easeInOut(duration: p.dur).repeatForever(autoreverses: true), value: petalDrift)
                }

                VStack(spacing: 22) {
                    sunMascot
                        .offset(y: sunRise)
                        .opacity(appeared ? 1 : 0)

                    wordmark
                        .opacity(appeared ? 1 : 0)
                        .offset(y: appeared ? 0 : 10)

                    Text("Your life awaits")
                        .font(.system(size: 17, weight: .semibold, design: .rounded))
                        .foregroundColor(textSoft)
                        .tracking(3)
                        .opacity(appeared ? 1 : 0)

                    loadingDots
                        .padding(.top, 6)
                        .opacity(appeared ? 1 : 0)
                }
                .position(x: w * 0.5, y: h * 0.46)
            }
        }
        .onAppear(perform: animateIn)
    }

    private var sunMascot: some View {
        ZStack {
            Circle()
                .fill(RadialGradient(colors: [gold.opacity(0.5), gold.opacity(0.18), .clear],
                                     center: .center, startRadius: 8, endRadius: 90))
                .frame(width: 180, height: 180)
                .scaleEffect(glowPulse)

            ForEach(0..<12, id: \.self) { i in
                Capsule()
                    .fill(gold.opacity(0.7))
                    .frame(width: 4, height: 13)
                    .offset(y: -64)
                    .rotationEffect(.degrees(Double(i) / 12 * 360))
                    .scaleEffect(glowPulse)
            }

            Circle()
                .fill(LinearGradient(colors: [Color.white, sunCore, gold],
                                     startPoint: .topLeading, endPoint: .bottomTrailing))
                .frame(width: 96, height: 96)
                .shadow(color: gold.opacity(0.6), radius: 18, y: 6)

            VStack(spacing: 6) {
                HStack(spacing: 18) { eye; eye }
                SplashSmile()
                    .stroke(pinkDeep.opacity(0.85), style: StrokeStyle(lineWidth: 3, lineCap: .round))
                    .frame(width: 24, height: 11)
            }
            .overlay(alignment: .center) {
                HStack(spacing: 44) {
                    Circle().fill(pink.opacity(0.5)).frame(width: 10, height: 10)
                    Circle().fill(pink.opacity(0.5)).frame(width: 10, height: 10)
                }
                .offset(y: 6)
            }
        }
        .frame(width: 180, height: 180)
    }

    private var eye: some View {
        Capsule().fill(Color(hex: 0x5A4A3A)).frame(width: 6, height: 10)
    }

    private var wordmark: some View {
        ZStack {
            Text("BaoLife")
                .font(.system(size: 52, weight: .bold, design: .rounded))
                .foregroundStyle(LinearGradient(colors: [pink, pinkDeep, Color(hex: 0xFFA0C4)],
                                                startPoint: .leading, endPoint: .trailing))
                .shadow(color: pink.opacity(0.25), radius: 8, y: 4)

            Text("BaoLife")
                .font(.system(size: 52, weight: .bold, design: .rounded))
                .foregroundStyle(.white)
                .mask(
                    Rectangle()
                        .fill(LinearGradient(colors: [.clear, .white.opacity(0.85), .clear],
                                             startPoint: .leading, endPoint: .trailing))
                        .rotationEffect(.degrees(18))
                        .offset(x: shimmerOffset)
                )
        }
    }

    private var loadingDots: some View {
        HStack(spacing: 7) {
            ForEach(0..<3, id: \.self) { i in
                Circle()
                    .fill(LinearGradient(colors: [pink, pinkDeep], startPoint: .top, endPoint: .bottom))
                    .frame(width: 8, height: 8)
                    .scaleEffect(dotPhase ? (i == 1 ? 1.25 : 0.7) : (i == 1 ? 0.7 : 1.25))
                    .opacity(dotPhase ? (i == 1 ? 1 : 0.5) : (i == 1 ? 0.5 : 1))
            }
        }
    }

    private func petalSpots(w: CGFloat, h: CGFloat) -> [(x: CGFloat, y: CGFloat, size: CGFloat, opacity: Double, drift: CGFloat, dur: Double)] {
        [(w * 0.16, h * 0.24, 20, 0.8, 14, 3.2),
         (w * 0.82, h * 0.30, 15, 0.7, 18, 3.8),
         (w * 0.74, h * 0.16, 13, 0.6, 12, 3.0),
         (w * 0.24, h * 0.62, 16, 0.6, 16, 4.1),
         (w * 0.88, h * 0.60, 12, 0.5, 20, 4.5)]
    }

    private func animateIn() {
        withAnimation(.spring(response: 0.7, dampingFraction: 0.7)) {
            appeared = true
            sunRise = 0
        }
        withAnimation(.easeInOut(duration: 1.6).repeatForever(autoreverses: true)) { glowPulse = 1.08 }
        withAnimation(.linear(duration: 2.4).repeatForever(autoreverses: false)) { shimmerOffset = 240 }
        withAnimation(.easeInOut(duration: 0.7).repeatForever(autoreverses: true)) { dotPhase = true }
        petalDrift = true
    }
}

/// A gentle upward smile arc for the splash sun's face.
private struct SplashSmile: Shape {
    func path(in rect: CGRect) -> Path {
        var p = Path()
        p.move(to: CGPoint(x: rect.minX, y: rect.minY))
        p.addQuadCurve(
            to: CGPoint(x: rect.maxX, y: rect.minY),
            control: CGPoint(x: rect.midX, y: rect.maxY * 1.6)
        )
        return p
    }
}

#Preview("Launch Splash") {
    LaunchScreenView()
}

// MARK: - Preview
// Preview removed - requires backend connection
