import APIClient
import ComposableArchitecture
import FeatureMadlibsOnboardingModels
import FeatureOnboardingModels
import OnboardingAnalyticsClient
import SwiftUI
import UIKit
import UserNotificationsClient
import Utilities

// MARK: - MadlibsContainerReducer

@Reducer
public struct MadlibsContainerReducer {
    @ObservableState
    public struct State: Equatable {
        let progressSteps = ProfileSetupStep.allCases.filter(\.includeInProgressBar)
        var currentStep: ProfileSetupStep = .musicGenre
        var me: Me

        @ObservationStateIgnored
        var previousStep: ProfileSetupStep?

        @ObservationStateIgnored
        var navigationDirection: NavigationDirection = .forward

        @ObservationStateIgnored @ObservedBox
        var musicGenreState: MusicGenrePicker.State

        @ObservationStateIgnored @ObservedBox
        var displayNameState: NameEntry.State?

        @ObservationStateIgnored @ObservedBox
        var usernameState: UsernameEntry.State?

        @ObservationStateIgnored @ObservedBox
        var birthdayState: MadlibsBirthdayEntry.State?

        @ObservationStateIgnored @ObservedBox
        var notificationsState: OnboardingNotificationsQuestion.State?

        var allSteps: [MadlibsContainerReducer.State.ProfileSetupStep] {
            ProfileSetupStep.allCases
        }

        func shouldShowProgress(for step: ProfileSetupStep) -> Bool {
            let isCompleted = completedSteps.contains(step)
            let isCurrent = step == currentStep
            return isCompleted || isCurrent
        }

        @ObservationStateIgnored
        var completedSteps: Set<ProfileSetupStep> = []

        @ObservationStateIgnored
        var notificationAllowStatus: PushNotificationAllowStatus?

        @ObservationStateIgnored
        var isRequestingNotificationPermission = false

        @ObservationStateIgnored
        var willSendCompleteFlow = false

        var selectedGenresCount: Int {
            musicGenreState.genres.filter(\.isSelected).count
        }

        var canNavigateNext: Bool {
            switch currentStep {
            case .musicGenre:
                return selectedGenresCount >= 1

            case .displayName:
                let isWorking = displayNameState?.isWorking ?? false
                let isEmpty = displayNameState?.value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
                return !isWorking && !isEmpty

            case .username:
                let isWorking = usernameState?.isWorking ?? false
                let isEmpty = usernameState?.value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
                let isCheckingOrPending = usernameState?.isCheckingOrPending ?? false
                let isAvailable = usernameState?.isUsernameAvailable ?? false
                return !isWorking && !isEmpty && !isCheckingOrPending && isAvailable

            case .birthday:
                let isWorking = birthdayState?.isWorking ?? false
                let isEmpty = birthdayState?.formattedValue.isEmpty ?? true
                let isValid = birthdayState?.isValidBirthday ?? false
                return !isWorking && !isEmpty && isValid

            case .notifications:
                return !isRequestingNotificationPermission && !willSendCompleteFlow
            }
        }

        var canSkip: Bool {
            let isCurrentStepWorking = switch currentStep {
            case .musicGenre:
                musicGenreState.isWorking
            case .displayName:
                displayNameState?.isWorking ?? false
            case .username:
                usernameState?.isWorking ?? false
            case .birthday:
                birthdayState?.isWorking ?? false
            case .notifications:
                isRequestingNotificationPermission
            }
            let notWorking = !isCurrentStepWorking
            let notAwatingFlowCompletion = !willSendCompleteFlow
            return notWorking && notAwatingFlowCompletion
        }

        var canNavigateBack: Bool {
            let isCurrentStepWorking = switch currentStep {
            case .musicGenre:
                false
            case .displayName:
                displayNameState?.isWorking ?? false
            case .username:
                usernameState?.isWorking ?? false
            case .birthday:
                birthdayState?.isWorking ?? false
            case .notifications:
                isRequestingNotificationPermission
            }
            let notWorking = !isCurrentStepWorking
            let notAwatingFlowCompletion = !willSendCompleteFlow
            return notWorking && notAwatingFlowCompletion
        }

        var isNextButtonLoading: Bool {
            let isCurrentStepWorking = switch currentStep {
            case .musicGenre:
                musicGenreState.isWorking
            case .displayName:
                displayNameState?.isWorking ?? false
            case .username:
                usernameState?.isWorking ?? false
            case .birthday:
                birthdayState?.isWorking ?? false
            case .notifications:
                isRequestingNotificationPermission
            }
            let isAwaitingFlowCompletion = willSendCompleteFlow
            return isCurrentStepWorking || isAwaitingFlowCompletion
        }

        var isSecondaryButtonLoading: Bool {
            switch currentStep {
            case .musicGenre,
                 .displayName,
                 .username,
                 .birthday:
                return false
            case .notifications:
                return isRequestingNotificationPermission || willSendCompleteFlow
            }
        }

        var shouldShowBackButton: Bool {
            switch currentStep {
            case .musicGenre:
                return false

            case .displayName,
                 .username,
                 .birthday,
                 .notifications:
                return true
            }
        }

        var shouldShowProgressBar: Bool {
            switch currentStep {
            case .musicGenre,
                 .displayName,
                 .username,
                 .birthday:
                return true

            case .notifications:
                return false
            }
        }

        var shouldShowSkipButton: Bool {
            switch currentStep {
            case .musicGenre,
                 .displayName,
                 .username,
                 .birthday:
                return true

            case .notifications:
                return false
            }
        }

        var shouldForceHideSecondaryButton: Bool {
            switch currentStep {
            case .musicGenre,
                 .displayName,
                 .username,
                 .birthday:
                return false

            case .notifications:
                let isAwaitingFlowCompletion = willSendCompleteFlow
                return isRequestingNotificationPermission || isAwaitingFlowCompletion
            }
        }

        var isTextInputStep: Bool {
            switch currentStep {
            case .displayName, .username:
                return true
            case .musicGenre, .birthday, .notifications:
                return false
            }
        }

        func shouldMaintainKeyboard(to nextStep: ProfileSetupStep) -> Bool {
            let textSteps = ProfileSetupStep.textInputSteps
            return textSteps.contains(currentStep) && textSteps.contains(nextStep)
        }

        public init(
            me: Me
        ) {
            self.me = me
            self.musicGenreState = MusicGenrePicker.State(me: me)
        }

        public enum ProfileSetupStep: CaseIterable, Equatable {
            case musicGenre
            case displayName
            case username
            case birthday
            case notifications

            fileprivate var includeInProgressBar: Bool {
                switch self {
                case .musicGenre,
                     .displayName,
                     .username,
                     .birthday:
                    true

                case .notifications:
                    false
                }
            }

            var next: ProfileSetupStep? {
                let steps = ProfileSetupStep.allCases
                guard let currentIndex = steps.firstIndex(of: self),
                      currentIndex + 1 < steps.count else { return nil }
                return steps[currentIndex + 1]
            }

            var previous: ProfileSetupStep? {
                let steps = ProfileSetupStep.allCases
                guard let currentIndex = steps.firstIndex(of: self),
                      currentIndex > 0 else { return nil }
                return steps[currentIndex - 1]
            }

            var toMadlibsStep: MadlibsOnboardingProfileStep {
                switch self {
                case .musicGenre: .musicGenre
                case .displayName: .displayName
                case .username: .username
                case .birthday: .birthday
                case .notifications: .notifications
                }
            }
        }

        enum NavigationDirection: Equatable {
            case forward
            case backward
        }
    }

    enum UserAction {
        case nextTapped
        case backTapped
        case skipTapped
    }

    @CasePathable
    public enum Action {
        case onAppear
        case onStepAppear(State.ProfileSetupStep)

        case nextTapped
        case backTapped
        case skipTapped

        case musicGenre(MusicGenrePicker.Action)
        case displayName(NameEntry.Action)
        case username(UsernameEntry.Action)
        case birthday(MadlibsBirthdayEntry.Action)
        case notifications(OnboardingNotificationsQuestion.Action)

        case navigation(Navigation)
        case delegate(Delegate)
        case `internal`(Internal)

        @CasePathable
        public enum Navigation: Equatable {
            case moveToStep(State.ProfileSetupStep)
            case goToPrevious
            case completeFlow(Me)
        }

        public enum HapticFeedbackType {
            case warning
            case light
            case medium
            case heavy
        }

        @CasePathable
        public enum Delegate: Equatable {
            case profileSetupCompleted(Me)
        }

        public enum Internal: Equatable {
            case notificationStatusUpdate(PushNotificationAllowStatus)
            case notificationPermissionRequestStarted
            case notificationPermissionRequestCompleted(Me)
            case willSendCompleteFlow
        }
    }

    struct NotificationStatusCancellable: Hashable {}

    @Dependency(UserNotificationClient.self) var userNotificationsClient
    @Dependency(\.onboardingAnalyticsClient) var onboardingAnalyticsClient

    public init() {}

    public var body: some ReducerOf<Self> {
        Scope(state: \.musicGenreState, action: \.musicGenre) {
            MusicGenrePicker()
        }

        Reduce { state, action in
            switch action {
            case .onAppear:
                state.willSendCompleteFlow = false
                onboardingAnalyticsClient.trackMadlibsEvent(.containerAppeared)
                return .none

            case .onStepAppear(let step):
                let madlibsStep = step.toMadlibsStep
                onboardingAnalyticsClient.trackMadlibsEvent(.stepViewed(step: madlibsStep))
                return .none

            case .nextTapped:
                return handleUserAction(.nextTapped, state: &state)

            case .backTapped:
                return handleUserAction(.backTapped, state: &state)

            case .skipTapped:
                return handleUserAction(.skipTapped, state: &state)

            case .navigation(let navigationAction):
                return handleNavigationAction(navigationAction, state: &state)

            case .internal(let internalAction):
                return handleInternalAction(internalAction, state: &state)

            case .musicGenre(.delegate(let delegateAction)):
                return handleMusicGenreDelegate(delegateAction, state: &state)

            case .displayName(.delegate(let delegateAction)):
                return handleDisplayNameDelegate(delegateAction, state: &state)

            case .username(.delegate(let delegateAction)):
                return handleUsernameDelegate(delegateAction, state: &state)

            case .birthday(.delegate(let delegateAction)):
                return handleBirthdayDelegate(delegateAction, state: &state)

            case .notifications(.delegate(let delegateAction)):
                return handleNotificationsDelegate(delegateAction, state: &state)

            case .musicGenre, .displayName, .username, .birthday, .notifications:
                return .none

            case .delegate:
                return .none
            }
        }
        .ifLet(\.displayNameState, action: \.displayName) {
            NameEntry()
        }
        .ifLet(\.usernameState, action: \.username) {
            UsernameEntry()
        }
        .ifLet(\.birthdayState, action: \.birthday) {
            MadlibsBirthdayEntry()
        }
        .ifLet(\.notificationsState, action: \.notifications) {
            OnboardingNotificationsQuestion()
        }
    }
}
