import AdamantiumClient
import APIClient
import AppSessionCountClient
import AppStartUpClient
import AttributionAndDeeplinkingClient
import BrazeClient
import ClerkClient
import Combine
import ComposableArchitecture
import DebugFeatureClient
import FirebaseClient
import Foundation
import InAppNotificationClient
import PaywallClient
import RemoteNotificationsClient
import ShareAssetClient
import StatsigClient
import UIKit
import UserNotifications
import UserNotificationsClient
import Utilities

@Reducer
public struct AppDelegateReducer {
    public struct State: Equatable {
        var ttiTrace: AnyCancellable?
        @Shared(.appStorage(.startUpOrchestratorEnabled)) var startUpOrchestratorEnabled: Bool = false
        var notificationAllowStatus: PushNotificationAllowStatus?
    }

    public enum Action {
        @CasePathable
        @dynamicMemberLookup
        public enum Delegate {
            case completedNotificationRegistration(_ granted: Bool)
            case hasAlreadyCompletedNotificationRegistration
        }

        case didFinishLaunching
        case didBecomeActive
        case registerForRemoteNotifications
        case didRegisterForRemoteNotifications(Result<Data, Error>)
        case didReceiveRemoteNotification(userInfo: [AnyHashable: Any], completionHandler: (UIBackgroundFetchResult) -> Void)
        case continueUserActivity(NSUserActivity)
        case onOpenURL(URL)

        case userNotifications(UserNotificationClient.DelegateEvent)
        case `internal`(Internal)
        case delegate(Delegate)

        public enum Internal {
            case error(Error)

            case clearTTITrace

            case notificationsAuthorizationStatusResponse(UNAuthorizationStatus)
            case notificationsRequestAuthorization
            case notificationsAuthorizationResponse(Result<Bool, Error>)
            case userNotificationsClientStatusUpdate(UserNotificationClient.NotificationAllowStatus)
        }
    }

    @Dependency(AdamantiumClient.self) var adamantium
    @Dependency(ShareAssetClient.self) var shareAssetClient
    @Dependency(\.firebaseClient) var firebase
    @Dependency(APIClient.self) var apiClient
    @Dependency(ClerkClient.self) var clerk
    @Dependency(\.telemetryClient) var telemetry
    @Dependency(UserNotificationClient.self) var userNotifications
    @Dependency(InAppNotificationClient.self) var inAppNotificationClient
    @Dependency(\.remoteNotifications.register) var registerForRemoteNotifications
    @Dependency(PaywallClient.self) var paywall
    @Dependency(StatsigClient.self) var statsig
    @Dependency(AttributionAndDeeplinkingClient.self) var attributionAndDeeplinkingClient
    @Dependency(BrazeClient.self) var braze
    @Dependency(DebugFeatureClient.self) var debugFeatureClient
    @Dependency(\.appSessionCountClient) var appSessionCountClient
    @Dependency(\.appStartUpClient) var appStartUpClient

    public init() {}

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            struct AppStartUpCancellableId: Hashable {}

            switch action {
            case .didFinishLaunching:
                log.info("didFinishLaunching")

                MainActor.assumeIsolated {
                    RemoteImagePrefetcher.configureNuke()
                }

                if state.startUpOrchestratorEnabled {
                    log.info("Starting new app start up process")
                    return .run { send in
                        do {
                            // Single blocking call - UI can't proceed until this completes
                            try await appStartUpClient.startup()

                            // Track session after startup is complete
                            await appSessionCountClient.trackFinishLaunching()

                            log.info("✅ App startup completed - UI can now proceed")
                        } catch {
                            log.telemetry.error(error, message: "❌ App startup failed.")
                            await send(.internal(.error(error)))
                        }
                    }
                } else {
                    log.info("Starting legacy app start up process")

                    // Initialize Firebase and TTI trace synchronously to match original behavior
                    MainActor.assumeIsolated {
                        firebase.configure()
                        state.ttiTrace = firebase.trace(.timeToInteractive)
                    }

                    return .run { send in
                        do {
                            // Initialize Braze on main thread as required by their SDK
                            let brazeTrace = firebase.trace(.sdk_Braze_Init)
                            try await MainActor.run {
                                try braze.configure()
                            }
                            brazeTrace.cancel()

                            // Initialize Paywall
                            let paywallTrace = firebase.trace(.sdk_RevenueCat_Init)
                            await MainActor.run {
                                paywall.configure()
                            }
                            paywallTrace.cancel()

                            log.info("firebase+braze configured")

                            // Track session after core services are initialized
                            await appSessionCountClient.trackFinishLaunching()

                            // Initialize remaining services
                            await firebase.withSpan(.sdksInitialization) {
                                await withTaskGroup(of: Void.self) { group in
                                    group.addTask(priority: .high) {
                                        do {
                                            try await clerk.configure()

                                            // Initialize Statsig with user info
                                            let statsigTrace = firebase.trace(.sdk_Statsig_Init)
                                            let statsigAsyncTrace = firebase.trace(.sdk_Statsig_Init)

                                            // Wait for Statsig initialization to complete
                                            await withCheckedContinuation { continuation in
                                                statsig.startStatsig() { error in
                                                    statsigAsyncTrace.cancel()

                                                    if let error {
                                                        log.telemetry.error(error)
                                                    } else {
                                                        // RageshakeClient requires StatsigClient to be setup
                                                        let isAllowed = debugFeatureClient.isDebugMenuAvailable()
                                                        debugFeatureClient.setRageshakeEnabled(isAllowed)
                                                    }
                                                    continuation.resume()
                                                }
                                            }
                                            statsigTrace.cancel()
                                        } catch {
                                            await send(.internal(.error(error)))
                                        }
                                    }

                                    group.addTask {
                                        await adamantium.configure()
                                        await shareAssetClient.configure()
                                    }
                                    group.addTask { attributionAndDeeplinkingClient.configure() }

                                    await group.waitForAll()
                                    log.debug("finished awaitingForAll")
                                }
                            }

                            log.debug("✅ Legacy app startup completed - UI can now proceed")
                        } catch {
                            log.telemetry.error(error, message: "❌ Legacy app startup failed.")
                            await send(.internal(.error(error)))
                        }
                    }
                }

            case .didBecomeActive:
                userNotifications.refreshPushNotificationAllowStatus()

                return .run { _ in
                    let authState: SessionAuthenticationState = (try? await clerk.user())
                        .map { SessionAuthenticationState.authenticated(userId: $0.id) }
                        ?? .unauthenticated
                    let _ = await appSessionCountClient.startSessionFromBecomeActive(authState)
                }

            case .continueUserActivity(let userActivity):
                attributionAndDeeplinkingClient.continueUserActivity(userActivity)
                return .none

            case .onOpenURL:
                // Handled in AppCoordinator
                return .none

            case .internal(.error(let error)):
                log.telemetry.error(error)
                return .none

            case .internal(.clearTTITrace):
                assert(state.ttiTrace != nil, "this was over-called. ttiTrace was already nilled out")
                state.ttiTrace = nil
                return .none

            case .registerForRemoteNotifications:
                return .run { send in
                    let status = await userNotifications.getNotificationSettings().authorizationStatus
                    await send(.internal(.notificationsAuthorizationStatusResponse(status)))
                }

            case .internal(.notificationsAuthorizationStatusResponse(let status)):
                switch status {
                case .notDetermined:
                    if FeatureFlag.local.askNotificationsOnRootView {
                        return .send(.internal(.notificationsRequestAuthorization))
                    } else {
                        return .none
                    }

                case .authorized, .provisional, .ephemeral:
                    return .concatenate(
                        .send(.delegate(.hasAlreadyCompletedNotificationRegistration)),
                        .run { _ in
                            await self.registerForRemoteNotifications()
                        }
                    )

                default:
                    return .send(.delegate(.completedNotificationRegistration(false)))
                }

            case .internal(.notificationsRequestAuthorization):
                return .run { send in
                    await send(.internal(.notificationsAuthorizationResponse(Result(catching: {
                        try await userNotifications.requestAuthorization([.alert, .badge, .sound])
                    }))))
                }

            case .internal(.notificationsAuthorizationResponse(.success(let granted))):
                userNotifications.refreshPushNotificationAllowStatus()
                guard granted else { return .send(.delegate(.completedNotificationRegistration(false))) }
                return .concatenate(
                    .send(.delegate(.completedNotificationRegistration(granted))),
                    .run { _ in
                        await self.registerForRemoteNotifications()
                    }
                )

            case .internal(.notificationsAuthorizationResponse(.failure(let error))):
                log.telemetry.error(error)
                return .send(.delegate(.completedNotificationRegistration(false)))

            case .didRegisterForRemoteNotifications(.failure(let error)):
                log.telemetry.error(error)
                return .none

            case let .didRegisterForRemoteNotifications(.success(tokenData)):
                return .run { _ in
                    braze.registerToken(tokenData)
                }

            case let .didReceiveRemoteNotification(userInfo, completionHandler):
                guard let sendableUserInfo = userInfo as? [AnyHashableSendable: Any] else {
                    completionHandler(.noData)
                    return .none
                }

                return .run { _ in
                    // Check if this is a Braze internal notification
                    if !braze.isInternalNotification(sendableUserInfo) {
                        // Handle custom silent push logic here
                        log.info("Received silent push notification: \(sendableUserInfo)")

                        // Perform background tasks for silent notifications
                        // Examples: sync data, update content, etc.

                        completionHandler(.newData)
                    } else {
                        // Let Braze handle its internal notifications
                        await braze.handleBackgroundNotification(sendableUserInfo, completionHandler)
                    }
                }

            case .internal(.userNotificationsClientStatusUpdate(let newStatus)):
                state.notificationAllowStatus = newStatus
                return .none

            case .userNotifications, .delegate:
                return .none
            }
        }
        .dependency(\.telemetryClient, firebase)

        Analytics()
    }
}
