import APIClient
import ComponentLibrary
import ComposableArchitecture
import Foundation
import Localization

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

        public var isWorking = false
        public var isFocused = true

        public var value = ""
        public var formattedValue = ""

        public var me: Me
        public var showBackButton = true
        public var showClearButton = true

        public var isValidBirthday: Bool {
            BirthdayFormatting.validateBirthday(formattedValue)
        }

        fileprivate var serverFormattedBirthday: String? {
            BirthdayFormatting.convertBirthdayToServerFormat(formattedValue)
        }

        public init(
            isWorking: Bool = false,
            me: Me,
            initialValue: String = "",
            showBackButton: Bool = true,
            showClearButton: Bool = true
        ) {
            self.isWorking = isWorking
            self.value = initialValue
            self.me = me
            self.showBackButton = showBackButton
            self.showClearButton = showClearButton
        }
    }

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

        @CasePathable
        @dynamicMemberLookup public enum Delegate {
            case onAppear
            case updateBirthdayComplete(Me)
            case backTapped
        }

        @CasePathable
        public enum Alert: Equatable {
            case dismiss
        }

        case onAppear
        case formatDate(String)
        case setFormattedDate(String)

        case updateBirthdayTapped
        case setFocus(Bool)

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

    @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 let .formatDate(value):
                let formatted = BirthdayFormatting.formatBirthdayInput(value)

                // Only update if the formatted value would be different
                if formatted != state.formattedValue {
                    return .send(.setFormattedDate(formatted))
                }
                return .none

            case let .setFormattedDate(formatted):
                state.formattedValue = formatted
                return .none

            case .updateBirthdayTapped:
                state.isWorking = true
                return .run(
                    operation: { [me = state.me, birthday = state.serverFormattedBirthday] send in
                        let newUser = try await api.postProfile(
                            ModifyArtistProfileSpec(birthDate: birthday),
                            me.user
                        )
                        await send(.internal(.updateBirthdayResult(.success(Me(models: me.models, roles: me.roles, flags: me.flags, user: newUser)))))
                    },
                    catch: { error, send in
                        await send(.internal(.updateBirthdayResult(.failure(error))))
                    }
                )

            case let .setFocus(value):
                state.isFocused = value
                return .none

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

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

                case let .failure(error):
                    state.alert = .init(
                        title: { TextState(L10n.FeatureOnboarding.errorTitle) },
                        actions: {
                            ButtonState { TextState(L10n.FeatureOnboarding.errorButton) }
                        },
                        message: {
                            if let apiError = error as? APIError, let errorDetail = apiError.errorDetail {
                                TextState(errorDetail)
                            } else {
                                TextState(error.underlyingError)
                            }
                        }
                    )
                    return .none
                }

            case .alert(.dismiss), .alert(.presented(.dismiss)):
                state.alert = nil
                return .none

            case .binding(\.value):
                return .none

            case .delegate, .binding:
                return .none
            }
        }
        .ifLet(\.$alert, action: \.alert)
    }
}
