import APIClient
import ClerkClient
import ComponentLibrary
import ComposableArchitecture
import DeeplinkIntents
import FeatureOnboardingModels
import FeatureOnboardingScreens
import Foundation
import InstanceIDClient
import Localization
import OnboardingAnalyticsClient
import StatsigClient
import SwiftUI
import Utilities

// MARK: - WelcomeCoordinator

@Reducer
public struct WelcomeCoordinator {
    @CasePathable
    public enum Content: Equatable {
        case welcome
        case onboardingV1(OnboardingCoordinator.State)
        case onboardingV2(OnboardingReducer.State)
    }

    public enum ForceOnboardingRoute: Equatable {
        case `default`
        case madlibs
    }

    // Wrap the auth error in this struct so we don't need to manually conform State to Equatable
    public struct AuthError: Equatable {
        var error: Error

        public init(error: Error) {
            self.error = error
        }

        public static func == (lhs: Self, rhs: Self) -> Bool {
            lhs.error.localizedDescription == rhs.error.localizedDescription
        }
    }

    // MARK: - State

    @ObservableState
    public struct State: Equatable {
        public var welcome = Welcome.State()
        public var content: Content = .welcome

        // The authenticated user (if returning user)
        public var me: Me?

        // First-time user experience flag
        public var isFTUX: Bool = false

        // Whether this is a returning user who might skip onboarding
        public var isReturningUser: Bool = false

        // Deeplink to handle after onboarding completes
        public var launchDeeplinkIntent: DeeplinkIntent?

        // Any error from authentication flow
        public var authError: AuthError?

        // Force routing to a specific onboarding flow (for debug menu)
        public var forceDebugRoute: ForceOnboardingRoute?

        public init(
            me: Me? = nil,
            isFTUX: Bool = false,
            isReturningUser: Bool = false,
            launchDeeplinkIntent: DeeplinkIntent? = nil,
            authError: AuthError? = nil,
            forceDebugRoute: ForceOnboardingRoute? = nil
        ) {
            self.me = me
            self.isFTUX = isFTUX
            self.isReturningUser = isReturningUser
            self.launchDeeplinkIntent = launchDeeplinkIntent
            self.authError = authError
            self.forceDebugRoute = forceDebugRoute
        }
    }

    // MARK: - Action

    public enum Action {
        case welcome(Welcome.Action)
        case onboardingV1(OnboardingCoordinator.Action)
        case onboardingV2(OnboardingReducer.Action)
        case delegate(Delegate)
        case `internal`(Internal)

        public enum Internal {
            case skipOnboardingForReturningUser(Me)
            case routeToOnboardingSystem(SignUp.Method)
        }

        public enum Delegate {
            case onboardingV1(OnboardingCoordinator.Action.Delegate)
            case onboardingV2(OnboardingReducer.Action.Delegate)
        }
    }

    // MARK: - Dependencies

    @Dependency(InstanceIDClient.self) private var instanceIdClient
    @Dependency(APIClientV2.self) private var api
    @Dependency(StatsigClient.self) private var statsigClient
    @Dependency(\.onboardingAnalyticsClient) private var onboardingAnalyticsClient

    public init() {}

    // MARK: - Reducer Body

    public var body: some ReducerOf<Self> {
        Scope(state: \.welcome, action: \.welcome) {
            Welcome()
        }

        Reduce { state, action in
            switch action {
            case let .welcome(.delegate(delegateAction)):
                return handleWelcomeDelegate(delegateAction, state: &state)

            case .welcome:
                return .none

            case .onboardingV1(.delegate(.backTappedOnSignUp)),
                 .onboardingV2(.delegate(.backTappedOnRoot)):
                state.content = .welcome
                return .none

            case let .onboardingV1(.delegate(delegateAction)):
                return .send(.delegate(.onboardingV1(delegateAction)))

            case let .onboardingV2(.delegate(delegateAction)):
                return .send(.delegate(.onboardingV2(delegateAction)))

            case let .internal(.routeToOnboardingSystem(method)):
                let isStatsigReady = state.welcome.isStatsigReady

                // Default routing logic based on feature flag
                // If statsig isn't ready, always default to the old onboarding coordinator
                // Check feature flag to determine which onboarding system to use
                if isStatsigReady, FeatureFlag.onboarding.useNewOnboardingSystem {
                    let onboardingState = OnboardingReducer.State(
                        flowVariant: .default,
                        deviceId: instanceIdClient.instanceId(),
                        initialMethod: method
                    )
                    state.content = .onboardingV2(onboardingState)
                } else {
                    let onboardingState = OnboardingCoordinator.State(
                        initialMethod: method,
                        error: state.authError?.error
                    )
                    state.content = .onboardingV1(onboardingState)
                }

                return .none

            case .delegate, .internal, .onboardingV1, .onboardingV2:
                return .none
            }
        }
        .ifLet(\.onboardingV1State, action: \.onboardingV1) {
            OnboardingCoordinator()
        }
        .ifLet(\.onboardingV2State, action: \.onboardingV2) {
            OnboardingReducer()
        }
    }
}

private extension WelcomeCoordinator {
    func handleWelcomeDelegate(
        _ action: Welcome.Action.Delegate,
        state: inout State
    ) -> Effect<Action> {
        switch action {
        case .onAppear:
            onboardingAnalyticsClient.trackWelcomePageAppeared()

            if let forceDebugRoute = state.forceDebugRoute {
                switch forceDebugRoute {
                case .default:
                    let onboardingState = OnboardingReducer.State(
                        flowVariant: .default,
                        deviceId: instanceIdClient.instanceId(),
                        initialMethod: .login
                    )
                    state.content = .onboardingV2(onboardingState)

                case .madlibs:
                    let onboardingState = OnboardingReducer.State.makeMadlibsDebug(
                        deviceId: instanceIdClient.instanceId()
                    )
                    state.content = .onboardingV2(onboardingState)
                }
            }

            return .none

        case .createAccountTapped:
            onboardingAnalyticsClient.trackSignUpTapped()
            return .send(.internal(.routeToOnboardingSystem(.create)))

        case .loginTapped:
            onboardingAnalyticsClient.trackSignInTapped()
            return .send(.internal(.routeToOnboardingSystem(.login)))
        }
    }
}

private extension WelcomeCoordinator.State {
    var onboardingV1State: OnboardingCoordinator.State? {
        get {
            guard case let .onboardingV1(state) = content else { return nil }
            return state
        }
        set {
            guard let newValue else { return }
            content = .onboardingV1(newValue)
        }
    }

    var onboardingV2State: OnboardingReducer.State? {
        get {
            guard case let .onboardingV2(state) = content else { return nil }
            return state
        }
        set {
            guard let newValue else { return }
            content = .onboardingV2(newValue)
        }
    }
}

// MARK: - WelcomeCoordinatorScreen

public struct WelcomeCoordinatorScreen: View {
    @Bindable var store: StoreOf<WelcomeCoordinator>

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

    public var body: some View {
        Group {
            switch store.content {
            case .welcome:
                WelcomeScreen(
                    store: store.scope(state: \.welcome, action: \.welcome)
                )

            case .onboardingV1:
                IfLetStore(
                    store.scope(
                        state: \.onboardingV1State,
                        action: \.onboardingV1
                    ),
                    then: OnboardingCoordinatorScreen.init(store:)
                )

            case .onboardingV2:
                IfLetStore(
                    store.scope(
                        state: \.onboardingV2State,
                        action: \.onboardingV2
                    ),
                    then: OnboardingScreen.init(store:)
                )
            }
        }
    }
}

extension WelcomeCoordinator.Action.Delegate: Equatable {
    public static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case let (.onboardingV1(lhsResult), .onboardingV1(rhsResult)):
            return lhsResult == rhsResult
        case let (.onboardingV2(lhsResult), .onboardingV2(rhsResult)):
            return lhsResult == rhsResult
        default:
            return false
        }
    }
}
