package kr.co.vividnext.sodalive.mypage.profile

import android.net.Uri
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.user.Gender
import kr.co.vividnext.sodalive.user.UserRepository
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File

class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewModel() {

    var youtubeUrl = ""
    var instagramUrl = ""
    var websiteUrl = ""
    var blogUrl = ""
    var introduce = ""

    var currentPassword = ""
    var newPassword = ""
    var newPasswordConfirm = ""

    val tags = mutableSetOf<String>()
    private val insertTags = mutableListOf<String>()
    private val removeTags = mutableListOf<String>()

    private lateinit var profileResponse: ProfileResponse

    private val _userInfoLiveData = MutableLiveData<ProfileResponse>()
    val userInfoLiveData: LiveData<ProfileResponse>
        get() = _userInfoLiveData

    private val _genderLiveData = MutableLiveData<Gender>()
    val genderLiveData: LiveData<Gender>
        get() = _genderLiveData

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

    private val _selectedTagLiveData = MutableLiveData<List<String>>()
    val selectedTagLiveData: LiveData<List<String>>
        get() = _selectedTagLiveData

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

    lateinit var getRealPathFromURI: (Uri) -> String?

    fun getUserInfo() {
        compositeDisposable.add(
            repository.getProfile("Bearer ${SharedPreferenceManager.token}")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            profileResponse = it.data
                            tags.addAll(profileResponse.tags)
                            _selectedTagLiveData.postValue(profileResponse.tags)
                            _genderLiveData.postValue(profileResponse.gender)
                            _userInfoLiveData.postValue(profileResponse)
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                    },
                    {
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }

    fun changeGender(gender: Gender) {
        _genderLiveData.postValue(gender)
    }

    fun updateProfileImage(uri: Uri, onSuccess: (String) -> Unit) {
        val file = File(getRealPathFromURI(uri))
        val image = MultipartBody.Part.createFormData(
            "image",
            file.name,
            file.asRequestBody("image/*".toMediaType())
        )

        compositeDisposable.add(
            repository.updateProfileImage(image, "Bearer ${SharedPreferenceManager.token}")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            onSuccess(it.data)
                            _toastLiveData.postValue("프로필 이미지가 변경되었습니다.")
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                    },
                    {
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }

    fun updateProfile(onSuccess: () -> Unit) {
        if (
            profileResponse.youtubeUrl != youtubeUrl ||
            profileResponse.instagramUrl != instagramUrl ||
            profileResponse.blogUrl != blogUrl ||
            profileResponse.websiteUrl != websiteUrl ||
            profileResponse.gender != _genderLiveData.value ||
            insertTags.isNotEmpty() ||
            removeTags.isNotEmpty() ||
            profileResponse.introduce != introduce
        ) {
            val request = ProfileUpdateRequest(
                email = profileResponse.email,
                nickname = null,
                youtubeUrl = if (profileResponse.youtubeUrl != youtubeUrl) {
                    youtubeUrl
                } else {
                    null
                },
                instagramUrl = if (profileResponse.instagramUrl != instagramUrl) {
                    instagramUrl
                } else {
                    null
                },
                blogUrl = if (profileResponse.blogUrl != blogUrl) {
                    blogUrl
                } else {
                    null
                },
                websiteUrl = if (profileResponse.websiteUrl != websiteUrl) {
                    websiteUrl
                } else {
                    null
                },
                gender = if (profileResponse.gender != _genderLiveData.value) {
                    _genderLiveData.value
                } else {
                    null
                },
                introduce = if (profileResponse.introduce != introduce) {
                    introduce
                } else {
                    null
                },
                insertTags = insertTags.ifEmpty { null },
                removeTags = removeTags.ifEmpty { null }
            )

            _isLoading.value = true

            compositeDisposable.add(
                repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                        {
                            if (it.success) {
                                _toastLiveData.postValue(
                                    "프로필이 변경되었습니다."
                                )
                                onSuccess()
                            } 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                        }
                    )
            )
        } else run {
            onSuccess()
        }
    }

    fun updatePassword(onSuccess: () -> Unit) {
        val email = SharedPreferenceManager.email

        if (currentPassword.isBlank()) {
            _toastLiveData.postValue("현재 비밀번호를 입력하세요")
            return
        }

        if (newPassword.isBlank()) {
            _toastLiveData.postValue("변경할 비밀번호를 입력하세요")
            return
        }

        if (newPasswordConfirm != newPassword) {
            _toastLiveData.postValue("비밀번호가 일치하지 않습니다.")
            return
        }

        val request = ProfileUpdateRequest(
            email = email,
            password = currentPassword,
            modifyPassword = newPassword
        )

        _isLoading.value = true

        compositeDisposable.add(
            repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success) {
                            _toastLiveData.postValue(
                                "비밀번호가 변경되었습니다."
                            )
                            onSuccess()
                        } 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 removeTag(tag: String) {
        tags.remove(tag)
        if (insertTags.contains(tag)) {
            insertTags.remove(tag)
        } else {
            removeTags.add(tag)
        }
        _selectedTagLiveData.postValue(tags.toList())
    }

    fun addTag(tag: String) {
        tags.add(tag)
        if (removeTags.contains(tag)) {
            removeTags.remove(tag)
        } else {
            insertTags.add(tag)
        }
        _selectedTagLiveData.postValue(tags.toList())
    }
}
