import AnalyticsClient
import AppSessionCountClient
import ComposableArchitecture

// swiftlint:disable file_length

extension OnboardingCoordinator {
    struct Analytics: AnalyticsReducer {
        var source: String = "onboarding_coordinator"

        /// These 2 flags are a code smell. this state should really be tracked on the backend instead
        /// For now: with we only want to fire these 2 analytic events if onboarding actually began and ended while in an app session. and we need it with very high confidence
        @Shared(.inMemory(.onboardingFlowStartedSent)) private var onboardingFlowStartedSent = false
        @Shared(.inMemory(.onboardingFlowEndedSent)) private var onboardingFlowEndedSent = false

        @Dependency(\.appSessionCountClient) private var appSessionCountClient

        // swiftlint:disable:next cyclomatic_complexity function_body_length
        func analytics(before: State, after: State, action: Action) -> Effect<Action> {
            switch action {
            case .getMe:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .loadCurrentUserStarted,
                        elementType: .user
                    )
                )

            case .internal(.finishSignIn(let me)):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signInFinished,
                        elementType: .user,
                        elementId: me.user.id
                    )
                )

            case .internal(.goToContactSync(let me)):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .onboardingContactSyncStarted,
                        elementType: .user,
                        elementId: me.user.id
                    )
                )

            case .analytics(.onboardingFlowStarted):
                if !onboardingFlowStartedSent {
                    self.$onboardingFlowStartedSent.withLock { $0 = true }
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .onboardingFlowStarted,
                            elementType: .user
                        )
                    )
                }

            case .delegate(.signUpResult(.success(let me))):
                Task.detached {
                    if await appSessionCountClient.isFirstTimeUser(me.user.id) {
                        track(
                            Event(
                                category: .onboarding,
                                actionName: .signUpSucceeded,
                                elementType: .user,
                                elementId: me.user.id
                            )
                        )
                    }
                }

                if onboardingFlowStartedSent && !onboardingFlowEndedSent {
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .onboardingFlowFinished,
                            elementType: .user,
                            elementId: me.user.id
                        )
                    )
                    self.$onboardingFlowEndedSent.withLock { $0 = true }
                }

            case .delegate(.signUpResult(.failure(let error))):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signUpFailed,
                        context: error.underlyingError
                    )
                )

            case .delegate(.signOut):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signedOut
                    )
                )

            case .path(.element(_, action: .usernameEntry(let usernameEntryAction))):
                let local_source: String = "username_entry"

                switch usernameEntryAction {
                case .updateUsernameTapped:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateHandleTapped,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .delegate(.backTapped):
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .backTapped,
                            actionType: .tap,
                            elementType: .toolbarButton
                        ),
                        sourceOverride: local_source
                    )

                case .internal(.updateUsernameResult(.success(let me))):
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateHandleSucceeded,
                            elementType: .handle,
                            elementId: me.user.handle
                        ),
                        sourceOverride: local_source
                    )

                case .internal(.updateUsernameResult(.failure(let error))):
                    guard case .nameEntry(let afterStore) = after.path.last else { return .none }
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateHandleFailed,
                            elementType: .handle,
                            elementId: afterStore.value,
                            context: error.underlyingError
                        ),
                        sourceOverride: local_source
                    )

                default:
                    break
                }
                return .none

            case .path(.element(_, action: .nameEntry(let nameEntryAction))):
                let local_source = "name_entry"

                switch nameEntryAction {
                case .updateNameTapped:
                    guard case .nameEntry(let beforeStore) = before.path.last else { return .none }
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateDisplayNameTapped,
                            actionType: .tap,
                            elementType: .button,
                            context: beforeStore.value
                        ),
                        sourceOverride: local_source
                    )

                case .internal(.updateNameResult(.success(let me))):
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateDisplayNameSucceeded,
                            elementType: .displayName,
                            elementId: me.user.displayName
                        ),
                        sourceOverride: local_source
                    )

                case .internal(.updateNameResult(.failure(let error))):
                    guard case .nameEntry(let afterStore) = after.path.last else { return .none }
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .updateDisplayNameFailed,
                            elementType: .displayName,
                            elementId: afterStore.value,
                            context: error.underlyingError
                        ),
                        sourceOverride: local_source
                    )

                case .delegate(.backTapped):
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .backTapped,
                            actionType: .tap,
                            elementType: .toolbarButton
                        ),
                        sourceOverride: local_source
                    )

                default:
                    break
                }

            case .path(.element(_, action: .birthday(.delegate(let birthdayAction)))):
                let local_source = "birthday_onboarding"

                switch birthdayAction {
                case .backTapped:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .backTapped,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .updateBirthdayComplete:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .birthdayUpdateCompleted,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .onAppear:
                    break
                }

            case .path(.element(_, action: .notificationQuestion(.delegate(let notificationPageAction)))):
                let local_source = "onboarding_notification"

                switch notificationPageAction {
                case .showNotification:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .notificationsPermissionsRequested,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .tappedBack:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .backTapped,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .tappedSkip:
                    track(
                        Event(
                            category: .onboarding,
                            actionName: .skipTapped,
                            actionType: .tap,
                            elementType: .button
                        ),
                        sourceOverride: local_source
                    )

                case .onAppear:
                    break
                }

            case .completedNotificationRegistration(let granted):
                let local_source = "onboarding_notification"
                track(
                    Event(
                        category: .onboarding,
                        actionName: granted ? .notificationsPermissionsAllowed : .notificationsPermissionsDenied,
                        actionType: .alert,
                        elementType: .button
                    ),
                    sourceOverride: local_source
                )

            case .hasAlreadyCompletedNotificationRegistration:
                /* Skip */
                break

            default:
                break
            }

            return .none
        }
    }
}

extension ContactsAuthorization {
    struct Analytics: AnalyticsReducer {
        var source: String = "contacts_authorization"

        func analytics(before _: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .showContactsAuthorizationAlert:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .contactsAuthorizationAlertShown,
                        actionType: .alert,
                        elementType: .alert
                    )
                )

            case .delegate(.authorized):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .contactPermissionsAllowed,
                        elementType: .permissions
                    )
                )

            case .delegate(.denied):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .contactPermissionsDenied,
                        elementType: .permissions
                    )
                )

            default:
                break
            }

            return .none
        }
    }
}

extension ContactSync {
    struct Analytics: AnalyticsReducer {
        var source: String = "contact_sync"

        func analytics(before _: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .delegate(.doneButtonTapped):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .doneTapped,
                        actionType: .tap,
                        elementType: .toolbarButton
                    )
                )

            default:
                break
            }

            return .none
        }
    }
}

extension SignUp {
    struct Analytics: AnalyticsReducer {
        var source: String = "sign_up"

        func analytics(before: State, after _: State, action: Action) -> Effect<Action> {
            let context: String = switch before.method {
            case .create:
                "create_account"
            case .login:
                "log_in"
            }

            switch action {
            case .signIn(let signInMethod):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signInTapped,
                        actionType: .tap,
                        elementType: .button,
                        // Can see if our copy is encouraging one way or the other
                        elementText: before.method.title,
                        method: signInMethodToString(signInMethod),
                        context: context
                    )
                )

            case .internal(.signInResponse(.success)):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signInSucceeded,
                        method: signInMethodToString(before.isWorking),
                        context: context
                    )
                )

            case .internal(.signInResponse(.failure)):
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signInFailed,
                        method: signInMethodToString(before.isWorking),
                        context: context
                    )
                )

            case .cancelTapped:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .cancelTapped,
                        actionType: .tap,
                        elementType: .toolbarButton,
                        context: context
                    )
                )

            default:
                break
            }

            return .none
        }

        private func signInMethodToString(_ signInMethod: SignInMethod?) -> String? {
            guard let signInMethod else {
                return nil
            }
            return switch signInMethod {
            case .phone:
                "phone"
            case .apple:
                "apple"
            case .google:
                "google"
            case .microsoft:
                "microsoft"
            case .discord:
                "discord"
            }
        }
    }
}

extension Welcome {
    struct Analytics: AnalyticsReducer {
        var source: String = "welcome"

        func analytics(before _: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .onAppear:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .welcomePageAppeared
                    )
                )

            case .createAccountTapped:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signUpTapped,
                        actionType: .tap,
                        elementType: .button
                    )
                )

            case .loginTapped:
                track(
                    Event(
                        category: .onboarding,
                        actionName: .signInTapped,
                        actionType: .tap,
                        elementType: .button
                    )
                )

            case .statsigStateChanged, .task:
                break

            case .delegate:
                break
            }

            return .none
        }
    }
}
