package kr.co.vividnext.sodalive.live.roulette.config

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.roulette.GetNewRouletteResponse
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
import kr.co.vividnext.sodalive.live.roulette.RoulettePreview
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem
import kr.co.vividnext.sodalive.live.roulette.RouletteRepository

class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() {

    enum class SelectedRoulette {
        ROULETTE_1, ROULETTE_2, ROULETTE_3
    }

    private var _isLoading = MutableLiveData(false)
    val isLoading: LiveData<Boolean>
        get() = _isLoading

    private val _toastLiveData = MutableLiveData<String?>()
    val toastLiveData: LiveData<String?>
        get() = _toastLiveData

    private val _optionsLiveData = MutableLiveData<List<RouletteOption>>(listOf())
    val optionsLiveData: LiveData<List<RouletteOption>>
        get() = _optionsLiveData

    private val _isActiveLiveData = MutableLiveData(false)
    val isActiveLiveData: LiveData<Boolean>
        get() = _isActiveLiveData

    private val _canLiveData = MutableLiveData(0)
    val canLiveData: LiveData<Int>
        get() = _canLiveData

    private val _roulettePreviewLiveData = MutableLiveData<RoulettePreview>()
    val roulettePreviewLiveData: LiveData<RoulettePreview>
        get() = _roulettePreviewLiveData

    private val _selectedRouletteLiveData = MutableLiveData<SelectedRoulette>()
    val selectedRouletteLiveData: LiveData<SelectedRoulette>
        get() = _selectedRouletteLiveData

    private val _totalPercentageLiveData = MutableLiveData(0f)
    val totalPercentageLiveData: LiveData<Float>
        get() = _totalPercentageLiveData

    var can = 0
    var isActive = false
    var availableActive = false
    private var rouletteId = 0L
    private val options = mutableListOf<RouletteOption>()

    val rouletteList = mutableListOf<GetNewRouletteResponse>()

    fun addOption(newOption: RouletteOption) {
        if (options.size >= 10) return
        options.add(newOption)
        _optionsLiveData.value = options
        calculateTotalPercentage()
    }

    fun deleteOption(index: Int) {
        val updatedOptions = options.filterIndexed { currentIndex, _ -> currentIndex != index }
        removeAllAndAddOptions(updatedOptions)
        _optionsLiveData.value = updatedOptions
    }

    fun inputOption(optionIndex: Int, title: String) {
        val currentOption = options[optionIndex]
        options[optionIndex] = currentOption.copy(title = title)
    }

    fun inputOptionPercentage(optionIndex: Int, percentage: String) {
        val currentOption = options[optionIndex]
        options[optionIndex] = currentOption.copy(percentage = percentage)
        calculateTotalPercentage()
    }

    fun toggleIsActive() {
        isActive = !isActive
        _isActiveLiveData.postValue(isActive)
    }

    fun onClickPreview() {
        _isLoading.value = true

        val items = mutableListOf<RoulettePreviewItem>()
        if (validationOptions()) {
            for (option in options) {
                if (option.title.trim().isEmpty()) {
                    _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
                    _isLoading.value = false
                    return
                }

                items.add(RoulettePreviewItem(option.title, "${option.percentage}%"))
            }

            _roulettePreviewLiveData.postValue(RoulettePreview(0, can, items))
        }

        _isLoading.value = false
    }

    fun createOrUpdateRoulette(onSuccess: (Boolean) -> Unit) {
        if (!_isLoading.value!!) {
            _isLoading.value = true

            if (validationOptions()) {
                if (rouletteId > 0) {
                    updateRoulette(onSuccess)
                } else {
                    createRoulette(onSuccess)
                }
            }
        }
    }

    private fun validationOptions(): Boolean {
        var totalPercentage = 0f
        for (option in options) {
            if (option.title.trim().isEmpty()) {
                _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
                _isLoading.value = false
                return false
            }

            if (option.percentage.isBlank()) {
                _toastLiveData.value = "옵션의 확률은 빈칸일 수 없습니다."
                _isLoading.value = false
                return false
            }

            if (option.percentage.toFloat() <= 0f) {
                _toastLiveData.value = "옵션의 확률은 0%보다 커야합니다."
                _isLoading.value = false
                return false
            }

            totalPercentage += option.percentage.toFloat()
        }

        if (totalPercentage > 100.1f || totalPercentage <= 99.99f) {
            _toastLiveData.value = "확률이 100%가 아닙니다"
            _isLoading.value = false
            return false
        }

        return true
    }

    private fun updateRoulette(onSuccess: (Boolean) -> Unit) {
        val items = mutableListOf<RouletteItem>()
        for (option in options) {
            items.add(RouletteItem(title = option.title, percentage = option.percentage.toFloat()))
        }

        val selectedRoulette = rouletteList[_selectedRouletteLiveData.value!!.ordinal]
        if (
            selectedRoulette.isActive == isActive &&
            selectedRoulette.can == can &&
            selectedRoulette.items == items
        ) {
            _toastLiveData.value = "변동사항이 없습니다."
            _isLoading.value = false
            return
        }

        val request = UpdateRouletteRequest(
            id = rouletteId,
            can = can,
            isActive = isActive,
            items = items
        )

        val selectedRouletteTitle = when (_selectedRouletteLiveData.value!!) {
            SelectedRoulette.ROULETTE_1 -> "룰렛 1"
            SelectedRoulette.ROULETTE_2 -> "룰렛 2"
            SelectedRoulette.ROULETTE_3 -> "룰렛 3"
        }

        val successMessage = if (availableActive) {
            if (isActive) {
                "${selectedRouletteTitle}을 활성화 했습니다."
            } else {
                "${selectedRouletteTitle}을 비활성화 했습니다."
            }
        } else {
            "${selectedRouletteTitle}을 변경했습니다."
        }

        compositeDisposable.add(
            repository.updateRoulette(
                request = request,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        _isLoading.value = false
                        if (it.success && it.data != null && it.data is Boolean) {
                            _toastLiveData.postValue(successMessage)
                            onSuccess(it.data)
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                    },
                    {
                        _isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }

    private fun createRoulette(onSuccess: (Boolean) -> Unit) {
        val items = mutableListOf<RouletteItem>()
        for (option in options) {
            items.add(RouletteItem(title = option.title, percentage = option.percentage.toFloat()))
        }

        val request = CreateRouletteRequest(
            can = can,
            isActive = isActive,
            items = items
        )

        val selectedRouletteTitle = when (_selectedRouletteLiveData.value!!) {
            SelectedRoulette.ROULETTE_1 -> "룰렛 1"
            SelectedRoulette.ROULETTE_2 -> "룰렛 2"
            SelectedRoulette.ROULETTE_3 -> "룰렛 3"
        }

        val successMessage = if (availableActive) {
            if (isActive) {
                "${selectedRouletteTitle}로 설정하였습니다."
            } else {
                "${selectedRouletteTitle}을 설정했습니다."
            }
        } else {
            "${selectedRouletteTitle}을 생성했습니다."
        }

        compositeDisposable.add(
            repository.createRoulette(
                request = request,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        _isLoading.value = false
                        if (it.success && it.data != null && it.data is Boolean) {
                            _toastLiveData.postValue(successMessage)
                            onSuccess(it.data)
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                    },
                    {
                        _isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }

    fun getAllRoulette() {
        if (!_isLoading.value!!) {
            _isLoading.value = true
            compositeDisposable.add(
                repository.getAllRoulette(
                    creatorId = SharedPreferenceManager.userId,
                    token = "Bearer ${SharedPreferenceManager.token}"
                )
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                        {
                            if (it.success) {
                                val data = it.data
                                rouletteList.clear()
                                rouletteList.addAll(data ?: listOf())
                                selectRoulette(SelectedRoulette.ROULETTE_1)
                            } else {
                                if (it.message != null) {
                                    _toastLiveData.postValue(it.message)
                                } else {
                                    _toastLiveData.postValue(
                                        "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                    )
                                }
                            }
                            _isLoading.value = false
                        },
                        {
                            _isLoading.value = false
                            it.message?.let { message -> Logger.e(message) }
                            _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                        }
                    )
            )
        }
    }

    fun selectRoulette(selectedRoulette: SelectedRoulette) {
        if (
            rouletteList.isEmpty() &&
            (
                selectedRoulette == SelectedRoulette.ROULETTE_2 ||
                    selectedRoulette == SelectedRoulette.ROULETTE_3
                )
        ) {
            _toastLiveData.value = "룰렛 1을 먼저 설정하세요"
            return
        }

        if (rouletteList.size == 1 && selectedRoulette == SelectedRoulette.ROULETTE_3) {
            _toastLiveData.value = "룰렛 1과 룰렛 2를 먼저 설정하세요"
            return
        }

        if (_selectedRouletteLiveData.value != selectedRoulette) {
            _selectedRouletteLiveData.value = selectedRoulette

            if (rouletteList.size > selectedRoulette.ordinal) {
                val roulette = rouletteList[selectedRoulette.ordinal]
                _canLiveData.value = roulette.can
                _isActiveLiveData.value = roulette.isActive

                can = roulette.can
                rouletteId = roulette.id
                isActive = roulette.isActive

                val options = roulette.items.asSequence().map { item ->
                    RouletteOption(title = item.title, percentage = item.percentage.toString())
                }.toList()
                removeAllAndAddOptions(options = options)
                _optionsLiveData.value = options
            } else {
                _canLiveData.value = 0
                _isActiveLiveData.value = false

                can = 0
                rouletteId = 0
                isActive = false

                options.clear()
                options.add(RouletteOption(title = "", percentage = ""))
                options.add(RouletteOption(title = "", percentage = ""))
                _optionsLiveData.value = options
            }

            calculateTotalPercentage()
        }
    }

    private fun removeAllAndAddOptions(options: List<RouletteOption>) {
        this.options.clear()
        this.options.addAll(options)
        calculateTotalPercentage()
    }

    private fun calculateTotalPercentage() {
        val totalPercent = options.map {
            try {
                it.percentage.toFloat()
            } catch (e: Exception) {
                0f
            }
        }.sum()

        _totalPercentageLiveData.value = totalPercent
    }
}
