package kr.co.vividnext.sodalive.audio_content.detail

import android.net.Uri
import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.firebase.dynamiclinks.ShortDynamicLink
import com.google.firebase.dynamiclinks.ktx.androidParameters
import com.google.firebase.dynamiclinks.ktx.dynamicLinks
import com.google.firebase.dynamiclinks.ktx.iosParameters
import com.google.firebase.dynamiclinks.ktx.shortLinkAsync
import com.google.firebase.dynamiclinks.ktx.socialMetaTagParameters
import com.google.firebase.ktx.Firebase
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.user.UserRepository

class AudioContentDetailViewModel(
    private val repository: AudioContentRepository,
    private val userRepository: UserRepository,
    private val authRepository: AuthRepository,
    private val reportRepository: ReportRepository,
    private val commentRepository: AudioContentCommentRepository
) : BaseViewModel() {

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

    var isLoading = MutableLiveData(false)
        private set

    private var _audioContentLiveData = MutableLiveData<GetAudioContentDetailResponse>()
    val audioContentLiveData: LiveData<GetAudioContentDetailResponse>
        get() = _audioContentLiveData

    private val _isExpandDetail = MutableLiveData(false)
    val isExpandDetail: LiveData<Boolean>
        get() = _isExpandDetail

    private val _isShowPreviewAlert = MutableLiveData(false)
    val isShowPreviewAlert: LiveData<Boolean>
        get() = _isShowPreviewAlert

    private val _isContentPlayLoopLiveData = MutableLiveData(
        SharedPreferenceManager.isContentPlayLoop
    )
    val isContentPlayLoopLiveData: LiveData<Boolean>
        get() = _isContentPlayLoopLiveData

    fun getAudioContentDetail(audioContentId: Long, onFailure: (() -> Unit)? = null) {
        if (!isLoading.value!!) {
            isLoading.value = true
        }

        compositeDisposable.add(
            repository.getAudioContentDetail(
                audioContentId = audioContentId,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            _audioContentLiveData.postValue(it.data!!)
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }

                            if (onFailure != null) {
                                onFailure()
                            }
                        }

                        isLoading.value = false
                    },
                    {
                        isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                        if (onFailure != null) {
                            onFailure()
                        }
                    }
                )
        )
    }

    fun follow(contentId: Long, creatorId: Long, follow: Boolean = true, notify: Boolean = true) {
        isLoading.value = true
        compositeDisposable.add(
            userRepository.creatorFollow(
                creatorId = creatorId,
                follow,
                notify,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            getAudioContentDetail(contentId)
                        } 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 registerNotification(contentId: Long, creatorId: Long) {
        isLoading.value = true
        compositeDisposable.add(
            repository.registerNotification(
                creatorId,
                "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            getAudioContentDetail(contentId)
                        } 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 unRegisterNotification(contentId: Long, creatorId: Long) {
        isLoading.value = true
        compositeDisposable.add(
            repository.unRegisterNotification(
                creatorId,
                "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            getAudioContentDetail(contentId)
                        } 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 toggleExpandDetail() {
        _isExpandDetail.value = !_isExpandDetail.value!!
    }

    fun toggleShowPreviewAlert() {
        _isShowPreviewAlert.value = !_isShowPreviewAlert.value!!
    }

    fun order(contentId: Long, orderType: OrderType, gotoShop: () -> Unit) {
        isLoading.value = true
        compositeDisposable.add(
            repository.orderContent(
                contentId = contentId,
                orderType = orderType,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success && it.data != null) {
                            getAudioContentDetail(audioContentId = contentId)
                            _toastLiveData.postValue(
                                if (orderType == OrderType.RENTAL) {
                                    "대여가 완료되었습니다."
                                } else {
                                    "구매가 완료되었습니다."
                                }
                            )
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                                if (it.message.contains("캔이 부족합니다")) {
                                    gotoShop()
                                }
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                        isLoading.value = false
                    },
                    {
                        isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }

    fun authVerify(audioContentId: Long, request: AuthVerifyRequest) {
        if (!isLoading.value!!) {
            isLoading.value = true
        }

        compositeDisposable.add(
            authRepository.verify(request, "Bearer ${SharedPreferenceManager.token}")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success) {
                            getAudioContentDetail(audioContentId)
                        } 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 registerComment(audioContentId: Long, comment: String, isSecret: Boolean) {
        if (!isLoading.value!!) {
            isLoading.value = true
        }

        compositeDisposable.add(
            commentRepository.registerComment(
                contentId = audioContentId,
                comment = comment,
                isSecret = isSecret,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        if (it.success) {
                            getAudioContentDetail(audioContentId)
                        } 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 likeContent(contentId: Long, onSuccess: (Boolean) -> Unit) {
        if (!isLoading.value!!) {
            isLoading.value = true
        }

        compositeDisposable.add(
            repository.likeContent(
                request = PutAudioContentLikeRequest(contentId = contentId),
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        isLoading.value = false

                        if (it.success) {
                            if (it.data != null) {
                                onSuccess(it.data.like)
                            } else {
                                getAudioContentDetail(contentId)
                            }
                        } 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 shareAudioContent(
        audioContentId: Long,
        contentImage: String,
        contentTitle: String,
        onSuccess: (String) -> Unit
    ) {
        isLoading.value = true
        Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
            link = Uri.parse("https://sodalive.net/?audio_content_id=$audioContentId")
            domainUriPrefix = "https://sodalive.page.link"
            androidParameters { }
            iosParameters("kr.co.vividnext.sodalive") {
                appStoreId = "6461721697"
            }
            socialMetaTagParameters {
                title = contentTitle
                description = "지금 보이스온에서 이 콘텐츠 감상하기"
                imageUrl = contentImage.toUri()
            }
        }.addOnSuccessListener {
            val uri = it.shortLink
            if (uri != null) {
                val message = uri.toString()
                onSuccess(message)
            } else {
                _toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.")
            }
        }.addOnFailureListener {
            _toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.")
        }.addOnCompleteListener {
            isLoading.value = false
        }
    }

    fun deleteAudioContent(audioContentId: Long, onSuccess: () -> Unit) {
        isLoading.value = true

        compositeDisposable.add(
            repository.deleteAudioContent(
                audioContentId,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        isLoading.value = false

                        if (it.success) {
                            _toastLiveData.postValue(
                                "삭제되었습니다."
                            )
                            onSuccess()
                        } 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 report(type: ReportType, contentId: Long, reason: String) {
        isLoading.value = true
        val request = ReportRequest(type = type, reason = reason, contentId = contentId)
        compositeDisposable.add(
            reportRepository.report(
                request = request,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        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 togglePlayLoop() {
        val isPlayLoop = !SharedPreferenceManager.isContentPlayLoop
        SharedPreferenceManager.isContentPlayLoop = isPlayLoop
        _isContentPlayLoopLiveData.value = isPlayLoop
    }

    fun donation(contentId: Long, can: Int, comment: String, onSuccess: () -> Unit) {
        isLoading.value = true
        compositeDisposable.add(
            repository.donation(
                contentId = contentId,
                can = can,
                comment = comment,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        isLoading.value = false

                        if (it.success) {
                            SharedPreferenceManager.can -= can
                            _toastLiveData.postValue(
                                "${can.moneyFormat()}캔을 후원하였습니다."
                            )
                            onSuccess()
                        } 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 pinContent(audioContentId: Long) {
        isLoading.value = true
        compositeDisposable.add(
            repository.pinContent(
                audioContentId,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        isLoading.value = false

                        if (it.success) {
                            _toastLiveData.postValue("고정되었습니다.")
                            getAudioContentDetail(audioContentId)
                        } 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 unPinContent(audioContentId: Long) {
        isLoading.value = true
        compositeDisposable.add(
            repository.unpinContent(
                audioContentId,
                token = "Bearer ${SharedPreferenceManager.token}"
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    {
                        isLoading.value = false

                        if (it.success) {
                            _toastLiveData.postValue("해제되었습니다.")
                            getAudioContentDetail(audioContentId)
                        } else {
                            if (it.message != null) {
                                _toastLiveData.postValue(it.message)
                            } else {
                                _toastLiveData.postValue(
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                                )
                            }
                        }
                    },
                    {
                        isLoading.value = false
                        it.message?.let { message -> Logger.e(message) }
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
                    }
                )
        )
    }
}
