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

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 HostViewModel @Inject constructor(
    private val bleMapper: BleMapper,
    private val writeBle: WriteBle,
    private val bleViewMapper: BleViewMapper
) : BaseViewModel<HostContract.State, HostContract.Event, HostContract.Effect>() {

    override fun setInitialState() = HostContract.State.Loading

    override fun handleEvents(event: HostContract.Event) {
        when(event){
            is HostContract.Event.OnNavigateUpClicked -> reduce(viewState.value, event)
            is HostContract.Event.OnTxChanged -> reduce(viewState.value, event)
            is HostContract.Event.OnBleChanged -> reduce(viewState.value, event)
            is HostContract.Event.OnChangePassword -> reduce(viewState.value, event)
            is HostContract.Event.OnHideWriteBlePreview -> reduce(viewState.value, event)
            is HostContract.Event.OnShowWriteBlePreview -> reduce(viewState.value, event)
            is HostContract.Event.OnWriteBle -> reduce(viewState.value, event)
            is HostContract.Event.OnPowerChanged -> reduce(viewState.value, event)
            is HostContract.Event.OnPowerEdit -> reduce(viewState.value, event)
            is HostContract.Event.OnShowHostHistory -> reduce(viewState.value, event)
            is HostContract.Event.OnShowHostBleTable -> reduce(viewState.value, event)
            is HostContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
            is HostContract.Event.OnShowIntervalEdit -> reduce(viewState.value, event)
            is HostContract.Event.OnSaveReadIntervalChanged -> reduce(viewState.value, event)
            is HostContract.Event.OnShowReadIntervalEdit -> reduce(viewState.value, event)
        }
    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnSaveReadIntervalChanged
    ) {

        if(state is HostContract.State.Display) {

            state.host.hostState.readInterval = event.interval

        }

        setEffect {
            HostContract.Effect.HideReadIntervalPicker
        }

    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnShowReadIntervalEdit
    ) {

        setEffect {
            HostContract.Effect.ShowReadIntervalPicker
        }

    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnSaveIntervalChanged
    ) {

        if(state is HostContract.State.Display) {

            state.host.hostState.historyInterval = event.interval

        }

        setEffect {
            HostContract.Effect.HideIntervalPicker
        }

    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnShowIntervalEdit
    ) {

        setEffect {
            HostContract.Effect.ShowIntervalPicker
        }

    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnShowHostBleTable
    ) {

        if(state is HostContract.State.Display) {

            setEffect {
                HostContract.Effect.Navigation.NavigateToBleTable(state.host.info.serial)
            }

        }

    }

    private fun reduce(
        state: HostContract.State,
        event: HostContract.Event.OnShowHostHistory
    ) {

        if(state is HostContract.State.Display) {

            setEffect {
                HostContract.Effect.Navigation.NavigateToHostHistory(state.host.info)
            }

        }

    }

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

        if(state is HostContract.State.Display) {

            state.host.state.tx = event.tx

        }

        setEffect {
            HostContract.Effect.HidePowerPicker
        }

    }


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


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

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

    }

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

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

    }

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

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

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

        if(state is HostContract.State.Display){

            val newBle = bleViewMapper.map(state.host) as Ble.Host

            val writeRequest = Ble.Host.WriteRequest(
                tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx,
                interval = if(newBle.hostState.historyInterval == state.origin.hostState.historyInterval) null else newBle.hostState.historyInterval,
                readInterval = if(newBle.hostState.readInterval == state.origin.hostState.readInterval) null else newBle.hostState.readInterval,
            )

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

            setEffect {
                HostContract.Effect.ShowWriteBlePreview
            }

        }

    }

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

        if(state is HostContract.State.Display){

            state.writeState?.let { request ->

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

                    viewModelScope.launch {

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

                        val currentState = viewState.value

                        if(currentState is HostContract.State.Display) {

                            val newBleObject = Ble.Host(
                                info = currentState.origin.info,
                                state = currentState.origin.state.copy(
                                    tx = request.writeRequest.tx ?: state.origin.state.tx
                                ),
                                hostState = currentState.origin.hostState.copy(
                                    historyInterval = request.writeRequest.interval
                                        ?: currentState.origin.hostState.historyInterval
                                )
                            )

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

                        }

                    }

                }

            }

        }

    }

}