import APIClient
import ComponentLibrary
import ComposableArchitecture
import Foundation
import Localization

@Reducer
public struct NameEntry {
    @ObservableState
    public struct State: Equatable {
        @Presents public var alert: AlertState<Action.Alert>?

        public var isWorking = false
        public var isFocused = false

        public var value: String = ""
        public var me: Me
        public var errorMessage: String?

        public let errorRepresentation: UserErrorRepresentation

        public init(
            isWorking: Bool = false,
            me: Me,
            initialValue: String? = nil,
            errorRepresentation: UserErrorRepresentation,
            isFocused _: Bool = false
        ) {
            self.isWorking = isWorking
            self.value = initialValue ?? ""
            self.me = me
            self.errorMessage = nil
            self.errorRepresentation = errorRepresentation
            self.isFocused = false
        }
    }

    public enum Action: BindableAction {
        public enum Internal {
            case updateNameResult(Result<Me, Error>)
        }

        @CasePathable
        @dynamicMemberLookup
        public enum Delegate {
            case onAppear
            case updateNameComplete(Me)
            case updateNameFailed(Error)
            case backTapped
            case displayNameFieldFocused
        }

        @CasePathable
        public enum Alert: Equatable {
            case dismiss
        }

        case onAppear
        case updateNameTapped
        case setFocus(Bool)
        case clearText

        case binding(BindingAction<State>)
        case `internal`(Internal)
        case delegate(Delegate)
        case alert(PresentationAction<Alert>)
    }

    public enum UserErrorRepresentation {
        case systemAlert
        case userFacingText
    }

    @Dependency(APIClient.self) var apiClient
    @Dependency(APIClientV2.self) var api

    public init() {}

    public var body: some ReducerOf<Self> {
        BindingReducer()

        Reduce { state, action in
            switch action {
            case .onAppear:
                return .send(.delegate(.onAppear))

            case .updateNameTapped:
                state.isWorking = true
                state.isFocused = false

                let name = state.value

                return .run(
                    operation: { [user = state.me.user] send in
                        try await apiClient.updateMe(.init(displayName: name, handle: user.handle))
                        let newMe = try await api.getMe()
                        await send(.internal(.updateNameResult(.success(newMe))))
                    },
                    catch: { error, send in
                        await send(.internal(.updateNameResult(.failure(error))))
                    }
                )

            case .setFocus(let value):
                state.isFocused = value
                if value {
                    // Field was focused, send delegate action
                    return .send(.delegate(.displayNameFieldFocused))
                }
                return .none

            case .clearText:
                state.value = ""
                return .none

            case .internal(.updateNameResult(let result)):
                state.isWorking = false

                switch result {
                case .success(let me):
                    return .send(.delegate(.updateNameComplete(me)))

                case .failure(let error):
                    switch state.errorRepresentation {
                    case .systemAlert:
                        state.alert = .init(
                            title: { TextState(L10n.FeatureOnboarding.errorTitle) },
                            actions: {
                                ButtonState { TextState(L10n.FeatureOnboarding.errorButton) }
                            },
                            message: { TextState(error.underlyingError) }
                        )

                    case .userFacingText:
                        state.errorMessage = error.underlyingError
                    }

                    state.isFocused = true
                    return .send(.delegate(.updateNameFailed(error)))
                }

            case .alert(.dismiss), .alert(.presented(.dismiss)):
                switch state.errorRepresentation {
                case .systemAlert:
                    state.alert = nil
                case .userFacingText:
                    break
                }

                state.isFocused = true
                return .none

            case .binding:
                // catch-all
                return .none

            case .delegate:
                // Handled upstream
                return .none
            }
        }
        .ifLet(\.$alert, action: \.alert)

        // Analytics()
    }
}
