package llc.arma.ble.app.ui.screen.inspection.beacon

import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.mapper.BleViewMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.WriteBle
import javax.inject.Inject

@HiltViewModel
class BeaconViewModel @Inject constructor(
    private val bleMapper: BleMapper,
    private val writeBle: WriteBle,
    private val bleViewMapper: BleViewMapper
) : BaseViewModel<BeaconContract.State, BeaconContract.Event, BeaconContract.Effect>() {

    override fun setInitialState() = BeaconContract.State.Loading

    override fun handleEvents(event: BeaconContract.Event) {
        when(event){
            is BeaconContract.Event.OnNavigateUpClicked -> reduce(viewState.value, event)
            is BeaconContract.Event.OnTxChanged -> reduce(viewState.value, event)
            is BeaconContract.Event.OnBleChanged -> reduce(viewState.value, event)
            is BeaconContract.Event.OnChangePassword -> reduce(viewState.value, event)
            is BeaconContract.Event.OnHideWriteBlePreview -> reduce(viewState.value, event)
            is BeaconContract.Event.OnShowWriteBlePreview -> reduce(viewState.value, event)
            is BeaconContract.Event.OnWriteBle -> reduce(viewState.value, event)
            is BeaconContract.Event.OnPowerChanged -> reduce(viewState.value, event)
            is BeaconContract.Event.OnPowerEdit -> reduce(viewState.value, event)
        }
    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnPowerChanged
    ) {

        if(state is BeaconContract.State.Display) {

            state.beacon.state.tx = event.tx

        }

        setEffect {
            BeaconContract.Effect.HidePowerPicker
        }

    }


    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnPowerEdit
    ) {
        setEffect { BeaconContract.Effect.ShowPowerPicker }
    }


    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnNavigateUpClicked
    ) {
        setEffect { BeaconContract.Effect.Navigation.NavigateUp }
    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnTxChanged
    ) {

    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnBleChanged
    ) {

        when(state){
            is BeaconContract.State.Display -> {
                setState {
                    state.copy(
                        origin = Ble.Beacon(
                            info = event.ble.info,
                            state = state.origin.state
                        )
                    )
                }
            }
            is BeaconContract.State.Loading -> {
                setState {
                    BeaconContract.State.Display(
                        origin = event.ble,
                        beacon = bleMapper.map(event.ble) as BleView.Beacon,
                        writeState = null
                    )
                }
            }
        }

    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnChangePassword
    ) {
        setEffect {
            BeaconContract.Effect.Navigation.NavigateToChangePassword
        }
    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnHideWriteBlePreview
    ) {
        setEffect {
            BeaconContract.Effect.HideWriteBlePreview
        }
    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnShowWriteBlePreview
    ) {

        if(state is BeaconContract.State.Display){

            val newBle = bleViewMapper.map(state.beacon) as Ble.Beacon

            val writeRequest = Ble.Beacon.WriteRequest(
                tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx
            )

            setState {
                state.copy(
                    writeState = BeaconContract.State.Display.WriteState.DisplayPreview(
                        writeRequest
                    )
                )
            }

            setEffect {
                BeaconContract.Effect.ShowWriteBlePreview
            }

        }

    }

    private fun reduce(
        state: BeaconContract.State,
        event: BeaconContract.Event.OnWriteBle
    ) {

        if(state is BeaconContract.State.Display){

            state.writeState?.let { request ->

                if(request is BeaconContract.State.Display.WriteState.DisplayPreview) {

                    viewModelScope.launch {

                        setState {
                            state.copy(
                                writeState = BeaconContract.State.Display.WriteState.Writing(request.writeRequest)
                            )
                        }

                        val currentState = viewState.value

                        if(currentState is BeaconContract.State.Display) {

                            val newBleObject = Ble.Beacon(
                                info = currentState.origin.info,
                                state = currentState.origin.state.copy(
                                    tx = request.writeRequest.tx ?: state.origin.state.tx
                                )
                            )

                            writeBle(state.beacon.info.serial, request.writeRequest).fold(
                                onSuccess = {
                                    setState {
                                        currentState.copy(
                                            origin = newBleObject,
                                            beacon = bleMapper.map(newBleObject) as BleView.Beacon,
                                            writeState = BeaconContract.State.Display.WriteState.Success
                                        )
                                    }
                                },
                                onFailure = {
                                    setState {
                                        state.copy(
                                            writeState = BeaconContract.State.Display.WriteState.Failure
                                        )
                                    }
                                }
                            )

                        }

                    }

                }

            }

        }

    }

}