package com.app.features.checkout import com.app.core.enums.DeliveryTime import com.app.core.enums.ShippingType import com.app.core.errors.AppError import com.app.core.models.* import com.app.core.states.BaseEvent import com.app.core.states.BaseSideEffect import com.app.core.states.BaseState import com.app.features.checkout.models.AppTempAddress import com.app.features.checkout.models.UIDeliveryAddress import com.payfort.sdk.android.dependancies.models.FortRequest import com.robustastudio.checkout_feat.CartPriceDetails import com.robustastudio.checkout_feat.PaymentKey import com.tinder.StateMachine sealed class CheckoutState : BaseState() { object UnInitialized : CheckoutState() //LOADING DELIVERY DAYS, INTERVALS, USER ADDRESSES, PAYMENT METHODS, PRICE DETAILS object LoadingCheckoutData : CheckoutState() class FailedToLoadCheckoutData(val error: AppError) : CheckoutState() data class CheckoutDataLoaded( val availableDeliveryDays: List?, val availableAddresses: List?, val isGuest: Boolean, val selectedShippingType: ShippingType, val hasNonGroceryItems: Boolean, val hasGroceryItems: Boolean, val selectedDeliveryAddress: UIDeliveryAddress, val selectedStore: AppShippingLocation? = null, val availableStores: List? = null, val availableCities: List? = null, val guestTempAddress: AppTempAddress? = null, val cartPriceDetails: CartPriceDetails, val isPaymentSectionLoading: Boolean = false, val selectedDeliveryTime: DeliveryTime = DeliveryTime.ASAP, val selectedDeliveryDay: AppDeliveryDay, val selectedDeliveryInterval: AppDeliveryInterval, val availablePaymentMethods: List?, val selectedPaymentMethod: AppAvailablePaymentMethod, val isCartLoading: Boolean = false, val isDeliveryExpanded: Boolean = true, val isPaymentExpanded: Boolean = true, val user: AppUser, val points: List> ) : CheckoutState() class Submitting( val availableDeliveryDays: List?, val availableAddresses: List?, val isGuest: Boolean, val selectedShippingType: ShippingType, val hasNonGroceryItems: Boolean, val hasGroceryItems: Boolean, val selectedDeliveryAddress: UIDeliveryAddress, val selectedStore: AppShippingLocation? = null, val availableStores: List? = null, val availableCities: List? = null, val guestTempAddress: AppTempAddress? = null, val cartPriceDetails: CartPriceDetails, val selectedDeliveryTime: DeliveryTime = DeliveryTime.ASAP, val selectedDeliveryDay: AppDeliveryDay, val selectedDeliveryInterval: AppDeliveryInterval, val availablePaymentMethods: List?, val selectedPaymentMethod: AppAvailablePaymentMethod, val user: AppUser, val points: List> ) : CheckoutState() class OrderCreated( val orderNumbers: List, ) : CheckoutState() } sealed class CheckoutEvent : BaseEvent() { object StartLoadingCheckoutData : CheckoutEvent() class OnFailedToLoadCheckoutData(val error: AppError) : CheckoutEvent() object OnPlaceOrderClicked : CheckoutEvent() object OnProceedToPayment : CheckoutEvent() object OnCouponStateChange : CheckoutEvent() object GoBackToShipping : CheckoutEvent() class PlaceOrderWithCC( val paymentKey: PaymentKey ) : CheckoutEvent() class OnPlaceOrderFailed(val error: AppError) : CheckoutEvent() class OnOrderPlacedSuccessfully(val orderNumbers: List) : CheckoutEvent() class OnShippingDataLoaded( val availableDeliveryDays: List?, val availableAddresses: List?, val isGuest: Boolean, val selectedShippingType: ShippingType, val hasNonGroceryItems: Boolean, val hasGroceryItems: Boolean, val selectedDeliveryAddress: UIDeliveryAddress, val selectedStore: AppShippingLocation? = null, val availableStores: List? = null, val cities: List? = null, val guestTempAddress: AppTempAddress? = null, val cartPriceDetails: CartPriceDetails, val availablePaymentMethods: List, val selectedPaymentMethod: AppAvailablePaymentMethod, val selectedDeliveryDay: AppDeliveryDay, val selectedDeliveryInterval: AppDeliveryInterval, val user: AppUser, val pointsRate: List> ) : CheckoutEvent() class OnPaymentMethodSelected( val paymentMethod: AppAvailablePaymentMethod ) : CheckoutEvent() class OnCartPricesUpdated( val cartPriceDetails: CartPriceDetails ) : CheckoutEvent() class OnDeliveryTimeSelected( val deliveryTime: DeliveryTime, val selectedDeliveryInterval: AppDeliveryInterval, val selectedDeliveryDay: AppDeliveryDay, val blockPlaceOrder: Boolean ) : CheckoutEvent() class OnAddressSelected(val shippingAddress: UIDeliveryAddress) : CheckoutEvent() class OnDeliveryDaysUpdated( val days: List, val selectedDeliveryInterval: AppDeliveryInterval, val selectedDeliveryDay: AppDeliveryDay ) : CheckoutEvent() class OnToggleDeliveryExpanded(val expanded: Boolean) : CheckoutEvent() class OnTogglePaymentExpanded(val expanded: Boolean) : CheckoutEvent() object OnPendingGateway : CheckoutEvent() object OnApplyLoyaltyPoints : CheckoutEvent() class OnApplyLoyaltyPointsSuccessfully( val cartPriceDetails: CartPriceDetails, val user: AppUser, ) : CheckoutEvent() class OnApplyLoyaltyPointsFailed(val error: AppError) : CheckoutEvent() } sealed class CheckoutSideEffect : BaseSideEffect() { class ShowError(val error: AppError) : CheckoutSideEffect() class ShowMinimumOrderAmountError(val minimumOrderAmount: Int) : CheckoutSideEffect() } fun getCheckoutStateMachine() = StateMachine.create { initialState(CheckoutState.UnInitialized) state { on { transitionTo(state = CheckoutState.LoadingCheckoutData) } } state { on { transitionTo( state = CheckoutState.CheckoutDataLoaded( availableAddresses = it.availableAddresses, isGuest = it.isGuest, selectedShippingType = it.selectedShippingType, selectedDeliveryAddress = it.selectedDeliveryAddress, selectedStore = it.selectedStore, availableStores = it.availableStores, availableCities = it.cities, guestTempAddress = it.guestTempAddress, cartPriceDetails = it.cartPriceDetails, availableDeliveryDays = it.availableDeliveryDays, availablePaymentMethods = it.availablePaymentMethods, selectedPaymentMethod = it.selectedPaymentMethod, hasNonGroceryItems = it.hasNonGroceryItems, hasGroceryItems = it.hasGroceryItems, selectedDeliveryInterval = it.selectedDeliveryInterval, selectedDeliveryDay = it.selectedDeliveryDay, user = it.user, points = it.pointsRate ) ) } on { event -> transitionTo(state = CheckoutState.FailedToLoadCheckoutData(event.error)) } } state { on { transitionTo(state = CheckoutState.LoadingCheckoutData) } } state { on { transitionTo(state = CheckoutState.LoadingCheckoutData) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = this.isPaymentSectionLoading, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isDeliveryExpanded = it.expanded, isPaymentExpanded = this.isPaymentExpanded, selectedDeliveryTime = this.selectedDeliveryTime, selectedDeliveryInterval = this.selectedDeliveryInterval, selectedDeliveryDay = this.selectedDeliveryDay, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = this.isPaymentSectionLoading, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = it.expanded, selectedDeliveryTime = this.selectedDeliveryTime, selectedDeliveryInterval = this.selectedDeliveryInterval, selectedDeliveryDay = this.selectedDeliveryDay, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = this.isPaymentSectionLoading, selectedDeliveryTime = it.deliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryDay = it.selectedDeliveryDay, selectedDeliveryInterval = it.selectedDeliveryInterval, isCartLoading = it.blockPlaceOrder, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, it.shippingAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = true, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( it.days, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, selectedDeliveryTime = DeliveryTime.ASAP, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryInterval = it.selectedDeliveryInterval, selectedDeliveryDay = it.selectedDeliveryDay, isCartLoading = false, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = true, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = it.paymentMethod, isCartLoading = true, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = it.cartPriceDetails, isPaymentSectionLoading = false, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = false, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( availableDeliveryDays = availableDeliveryDays, availableAddresses = availableAddresses, isGuest = this.isGuest, selectedShippingType = this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, selectedDeliveryAddress = this.selectedDeliveryAddress, selectedStore = this.selectedStore, availableStores = this.availableStores, availableCities = this.availableCities, guestTempAddress = this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = true, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = true, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, isDeliveryExpanded = this.isDeliveryExpanded, isPaymentExpanded = this.isPaymentExpanded, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.Submitting( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points ) ) } on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points, isCartLoading = true ) ) } on { transitionTo(state = CheckoutState.LoadingCheckoutData) } on { event -> transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = event.cartPriceDetails, isPaymentSectionLoading = false, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = false, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = event.user, points = this.points ) ) } on { event -> transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = false, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = false, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points ), CheckoutSideEffect.ShowError(event.error) ) } } state { on { transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = true, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = false, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points ) ) } on { event -> transitionTo( state = CheckoutState.CheckoutDataLoaded( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, isPaymentSectionLoading = true, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, isCartLoading = false, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points ), CheckoutSideEffect.ShowError(event.error) ) } on { event -> transitionTo( state = CheckoutState.Submitting( this.availableDeliveryDays, this.availableAddresses, this.isGuest, this.selectedShippingType, hasNonGroceryItems = this.hasNonGroceryItems, hasGroceryItems = this.hasGroceryItems, this.selectedDeliveryAddress, this.selectedStore, this.availableStores, this.availableCities, this.guestTempAddress, cartPriceDetails = this.cartPriceDetails, selectedDeliveryTime = this.selectedDeliveryTime, availablePaymentMethods = this.availablePaymentMethods, selectedPaymentMethod = this.selectedPaymentMethod, selectedDeliveryDay = this.selectedDeliveryDay, selectedDeliveryInterval = this.selectedDeliveryInterval, user = this.user, points = this.points ) ) } on { event -> transitionTo( state = CheckoutState.OrderCreated( event.orderNumbers ) ) } } state { } } sealed class CheckoutClickActions { class ProceedWithPayFortPayment( val fortRequest: FortRequest ) : CheckoutClickActions() class ProceedWithPaymobPayment( val iFrameUrl: String? = null, val referenceNumber: String? = null ) class VerifyAddress(val address: AppShippingAddress) : CheckoutClickActions() }