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

import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.domain.model.BleName
import llc.arma.ble.domain.usecase.AddBleToHostTable
import llc.arma.ble.domain.usecase.GetBleNamesFlow
import llc.arma.ble.domain.usecase.GetFoundBle
import llc.arma.ble.domain.usecase.GetHostBleTableBySerial
import javax.inject.Inject

@HiltViewModel
class BleTableEditViewModel @Inject constructor(
    getFoundBle: GetFoundBle,
    private val getBleNamesFlow: GetBleNamesFlow,
    private val addBleToHostTable: AddBleToHostTable,
    private val getHostBleTableBySerial: GetHostBleTableBySerial
) : BaseViewModel<BleTableEditContract.State, BleTableEditContract.Event, BleTableEditContract.Effect>() {

    private var lastSerial: String = ""

    init {

        viewModelScope.launch {

            while (true){

                val state = viewState.value

                if(state is BleTableEditContract.State.Display) {

                    setState {
                        state.copy(bleAround = getFoundBle())
                    }

                }
                delay(1_000)


            }

        }

    }

    override fun setInitialState() = BleTableEditContract.State.Loading

    override fun handleEvents(event: BleTableEditContract.Event) {
        when(event){
            is BleTableEditContract.Event.OnStart -> reduce(viewState.value, event)
            is BleTableEditContract.Event.OnAddBle -> reduce(viewState.value, event)
            is BleTableEditContract.Event.OnWritePreview -> reduce(viewState.value, event)
            is BleTableEditContract.Event.OnHideWritePreview -> reduce(viewState.value, event)
            is BleTableEditContract.Event.OnWrite -> reduce(viewState.value, event)
        }
    }

    private fun reduce(
        state: BleTableEditContract.State,
        event: BleTableEditContract.Event.OnWrite
    ) {

        if(state is BleTableEditContract.State.Display) {

            viewModelScope.launch {

                setState {
                    state.copy(
                        writeState = BleTableEditContract.State.Display.WriteState.Writing(state.newTable)
                    )
                }

                addBleToHostTable.invoke(
                    serial = lastSerial,
                    ble = state.newTable
                ).fold(
                    onSuccess = {
                        setState {
                            state.copy(
                                writeState = BleTableEditContract.State.Display.WriteState.Success
                            )
                        }
                        setEvent(BleTableEditContract.Event.OnStart(lastSerial))
                    },
                    onFailure = {
                        setState {
                            state.copy(
                                writeState = BleTableEditContract.State.Display.WriteState.Failure
                            )
                        }
                    }
                )

            }

        }

    }

    private fun reduce(
        state: BleTableEditContract.State,
        event: BleTableEditContract.Event.OnHideWritePreview
    ) {

        if(state is BleTableEditContract.State.Display) {

            setState {
                state.copy(writeState = null)
            }

        }

    }

    private fun reduce(
        state: BleTableEditContract.State,
        event: BleTableEditContract.Event.OnWritePreview
    ) {

        if(state is BleTableEditContract.State.Display) {

            setState {
                state.copy(writeState = BleTableEditContract.State.Display.WriteState.DisplayPreview(state.newTable))
            }

        }

    }

    private fun reduce(
        state: BleTableEditContract.State,
        event: BleTableEditContract.Event.OnAddBle
    ) {

        if(state is BleTableEditContract.State.Display) {

            if(state.newTable.any { it.serial == event.ble.serial}){

                setState {
                    state.copy(newTable = state.newTable.filter { it.serial != event.ble.serial})
                }

            } else {

                setState {
                    state.copy(newTable = state.newTable.toMutableList().apply { add(event.ble) })
                }

            }

        }

    }

    private fun reduce(
        state: BleTableEditContract.State,
        event: BleTableEditContract.Event.OnStart
    ) {

        lastSerial = event.serial

        setState {
            BleTableEditContract.State.Loading
        }

        viewModelScope.launch {

            val names = getBleNamesFlow.invoke().first()

            getHostBleTableBySerial(event.serial).fold(
                onSuccess = {

                    val savedBle = it.map { ble -> BleName(
                        name = names.firstOrNull { it.serial == ble }?.name ?: "Безымянный",
                        serial = ble) }

                    setState {
                        BleTableEditContract.State.Display(
                            bleAround = emptyList(),
                            newTable = savedBle,
                            savedBleTable = savedBle,
                            writeState = null)
                    }

                },
                onFailure = {
                    setState {
                        BleTableEditContract.State.Error
                    }
                }
            )

        }

    }

}