package kr.co.vividnext.sodalive.live.room.create

import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
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.LiveRepository
import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.util.TimeZone

class LiveRoomCreateViewModel(
    private val repository: LiveRepository
) : BaseViewModel() {

    enum class SelectedMenu {
        MENU_1, MENU_2, MENU_3
    }

    private val _roomTypeLiveData = MutableLiveData(LiveRoomType.OPEN)
    val roomTypeLiveData: LiveData<LiveRoomType>
        get() = _roomTypeLiveData

    private val _timeNowLiveData = MutableLiveData(true)
    val timeNowLiveData: LiveData<Boolean>
        get() = _timeNowLiveData

    private val _reservationDateLiveData = MutableLiveData("날짜를 선택해주세요")
    val reservationDateLiveData: LiveData<String>
        get() = _reservationDateLiveData

    private val _reservationTimeLiveData = MutableLiveData("시간을 설정해주세요")
    val reservationTimeLiveData: LiveData<String>
        get() = _reservationTimeLiveData

    private val _selectedLiveData = MutableLiveData<List<String>>()
    val selectedLiveData: LiveData<List<String>>
        get() = _selectedLiveData

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

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

    private val _priceLiveData = MutableLiveData(0)
    val priceLiveData: LiveData<Int>
        get() = _priceLiveData

    private val _isAdultLiveData = MutableLiveData(false)
    val isAdultLiveData: LiveData<Boolean>
        get() = _isAdultLiveData

    private val _selectedMenuLiveData = MutableLiveData<SelectedMenu>()
    val selectedMenuLiveData: LiveData<SelectedMenu>
        get() = _selectedMenuLiveData

    private val _isActivateMenuLiveData = MutableLiveData(false)
    val isActivateMenuLiveData: LiveData<Boolean>
        get() = _isActivateMenuLiveData

    private val _isAvailableJoinCreatorLiveData = MutableLiveData(true)
    val isAvailableJoinCreatorLiveData: LiveData<Boolean>
        get() = _isAvailableJoinCreatorLiveData

    private val _menuLiveData = MutableLiveData("")
    val menuLiveData: LiveData<String>
        get() = _menuLiveData

    lateinit var getRealPathFromURI: (Uri) -> String?

    var title = ""
    var content = ""
    var numberOfPeople = 0
    var tags = mutableSetOf<String>()
    var beginDate = ""
    var beginTime = ""
    var coverImageUri: Uri? = null
    var coverImagePath: String? = null
    var password: String? = null

    private var menuId = 0L
    var menu = ""
    val menuList = mutableListOf<GetMenuPresetResponse>()

    fun setRoomType(roomType: LiveRoomType) {
        if (_roomTypeLiveData.value!! != roomType) {
            _roomTypeLiveData.postValue(roomType)
        }
    }

    fun setTimeNow(timeNow: Boolean) {
        _timeNowLiveData.postValue(timeNow)
    }

    fun setReservationDate(dateString: String) {
        _reservationDateLiveData.postValue(dateString)
    }

    fun setReservationTime(timeString: String) {
        _reservationTimeLiveData.postValue(timeString)
    }

    fun createLiveRoom(onSuccess: (CreateLiveRoomResponse) -> Unit) {
        if (!_isLoading.value!! && validateData()) {
            _isLoading.postValue(true)
            val request = CreateLiveRoomRequest(
                title = title,
                price = _priceLiveData.value!!,
                content = content,
                coverImageUrl = coverImagePath,
                isAdult = _isAdultLiveData.value!!,
                tags = tags.toList(),
                numberOfPeople = numberOfPeople,
                beginDateTimeString = if (
                    !_timeNowLiveData.value!!
                ) {
                    "$beginDate $beginTime"
                } else {
                    null
                },
                timezone = TimeZone.getDefault().id,
                type = _roomTypeLiveData.value!!,
                password = if (
                    _roomTypeLiveData.value!! == LiveRoomType.PRIVATE &&
                    password != null
                ) {
                    password
                } else {
                    null
                },
                menuPanId = if (_isActivateMenuLiveData.value!!) {
                    menuId
                } else {
                    0
                },
                menuPan = if (_isActivateMenuLiveData.value!!) {
                    menu
                } else {
                    ""
                },
                isActiveMenuPan = _isActivateMenuLiveData.value!!,
                isAvailableJoinCreator = _isAvailableJoinCreatorLiveData.value!!
            )

            val requestJson = Gson().toJson(request)

            val coverImage = if (coverImageUri != null) {
                val file = File(getRealPathFromURI(coverImageUri!!))
                MultipartBody.Part.createFormData(
                    "coverImage",
                    file.name,
                    file.asRequestBody("image/*".toMediaType())
                )
            } else {
                null
            }
            compositeDisposable.add(
                repository.createRoom(
                    coverImage,
                    requestJson.toRequestBody("text/plain".toMediaType()),
                    "Bearer ${SharedPreferenceManager.token}"
                )
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                        {
                            if (it.success && it.data != null) {
                                onSuccess(it.data!!)
                            } else {
                                if (it.message != null) {
                                    _toastLiveData.postValue(it.message)
                                } else {
                                    _toastLiveData.postValue(
                                        "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                    )
                                }
                            }
                            _isLoading.postValue(false)
                        },
                        {
                            _isLoading.postValue(false)
                            it.message?.let { message -> Logger.e(message) }
                            _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                        }
                    )
            )
        }
    }

    fun removeTag(tag: String) {
        tags.remove(tag)
        _selectedLiveData.postValue(tags.toList())
    }

    fun addTag(tag: String) {
        tags.add(tag)
        _selectedLiveData.postValue(tags.toList())
    }

    private fun validateData(): Boolean {
        if (title.isBlank()) {
            _toastLiveData.postValue("제목을 입력해주세요.")
            return false
        }

        if (content.isBlank() || content.length < 5) {
            _toastLiveData.postValue("내용을 5자 이상 입력해주세요.")
            return false
        }

        if (numberOfPeople < 3 || numberOfPeople > 999) {
            _toastLiveData.postValue("인원을 3~999명 사이로 입력해주세요.")
            return false
        }

        if (coverImageUri == null && coverImagePath == null) {
            _toastLiveData.postValue("커버이미지를 선택해주세요.")
            return false
        }

        if (!_timeNowLiveData.value!! && (beginDate.isBlank() || beginTime.isBlank())) {
            _toastLiveData.postValue("예약날짜와 시간을 선택해주세요.")
            return false
        }

        if (
            _roomTypeLiveData.value!! == LiveRoomType.PRIVATE &&
            (password == null || password!!.length != 6)
        ) {
            _toastLiveData.postValue("방 입장 비밀번호 6자리를 입력해 주세요.")
            return false
        }

        if (_isActivateMenuLiveData.value!! && menu.isBlank()) {
            _toastLiveData.postValue("메뉴판은 빈칸일 수 없습니다.")
            return false
        }

        return true
    }

    fun setPrice(price: Int) {
        _priceLiveData.value = price
    }

    fun setAdult(isAdult: Boolean) {
        _isAdultLiveData.value = isAdult
    }

    fun setAvailableJoinCreator(isAvailableJoinCreator: Boolean) {
        _isAvailableJoinCreatorLiveData.value = isAvailableJoinCreator
    }

    fun getRecentInfo(onSuccess: (GetRecentRoomInfoResponse) -> Unit) {
        _isLoading.value = true
        compositeDisposable.add(
            repository.getRecentRoomInfo(token = "Bearer ${SharedPreferenceManager.token}")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            coverImageUri = null
                            coverImagePath = it.data.coverImagePath
                            onSuccess(it.data!!)

                            _toastLiveData.postValue("최근데이터를 불러왔습니다.")
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "최근데이터를 불러오지 못했습니다.\n다시 시도해 주세요."
                                )
                            }
                        }

                        _isLoading.value = false
                    },
                    {
                        _isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("최근데이터를 불러오지 못했습니다.\n다시 시도해 주세요.")
                    }
                )
        )
    }

    fun selectMenuPreset(selectedMenuPreset: SelectedMenu) {
        if (
            menuList.isEmpty() &&
            (
                selectedMenuPreset == SelectedMenu.MENU_2 ||
                    selectedMenuPreset == SelectedMenu.MENU_3
                )
        ) {
            _toastLiveData.value = "메뉴 1을 먼저 설정하세요"
            return
        }

        if (menuList.size == 1 && selectedMenuPreset == SelectedMenu.MENU_3) {
            _toastLiveData.value = "메뉴 1과 메뉴 2를 먼저 설정하세요"
            return
        }

        if (_selectedMenuLiveData.value != selectedMenuPreset) {
            _selectedMenuLiveData.value = selectedMenuPreset

            if (menuList.size > selectedMenuPreset.ordinal) {
                val menuPreset = menuList[selectedMenuPreset.ordinal]
                _menuLiveData.value = menuPreset.menu

                menu = menuPreset.menu
                menuId = menuPreset.id
            } else {
                _menuLiveData.value = ""

                menu = ""
                menuId = 0
            }
        }
    }

    fun toggleIsActivateMenu() {
        _isActivateMenuLiveData.value = !_isActivateMenuLiveData.value!!
    }

    fun getAllMenuPreset() {
        _isLoading.value = true

        compositeDisposable.add(
            repository.getAllMenu(
                creatorId = SharedPreferenceManager.userId,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success) {
                            val data = it.data
                            menuList.clear()
                            menuList.addAll(data ?: listOf())
                            selectMenuPreset(SelectedMenu.MENU_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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }
}
