package kr.co.vividnext.sodalive.audio_content.upload

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.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.PurchaseOption
import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone

class AudioContentUploadViewModel(
    private val repository: AudioContentRepository
) : BaseViewModel() {

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

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

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

    private val _purchaseOptionLiveData = MutableLiveData(PurchaseOption.BOTH)
    val purchaseOptionLiveData: LiveData<PurchaseOption>
        get() = _purchaseOptionLiveData

    private val _isAvailableCommentLiveData = MutableLiveData(true)
    val isAvailableCommentLiveData: LiveData<Boolean>
        get() = _isAvailableCommentLiveData

    private val _isPriceFreeLiveData = MutableLiveData(true)
    val isPriceFreeLiveData: LiveData<Boolean>
        get() = _isPriceFreeLiveData

    private val _isLimitedLiveData = MutableLiveData(false)
    val isLimitedLiveData: LiveData<Boolean>
        get() = _isLimitedLiveData

    private val _isShowConfigLimitedLiveData = MutableLiveData(false)
    val isShowConfigLimitedLiveData: LiveData<Boolean>
        get() = _isShowConfigLimitedLiveData

    private val _isGeneratePreviewLiveData = MutableLiveData(true)
    val isGeneratePreviewLiveData: LiveData<Boolean>
        get() = _isGeneratePreviewLiveData

    private val _isActiveReservationLiveData = MutableLiveData(false)
    val isActiveReservationLiveData: LiveData<Boolean>
        get() = _isActiveReservationLiveData

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

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

    lateinit var getRealPathFromURI: (Uri) -> String?

    var title = ""
    var detail = ""
    var tags = ""
    var price = 0
    var limited = 0
    var releaseDate = ""
    var releaseTime = ""
    var theme: GetAudioContentThemeResponse? = null
    var coverImageUri: Uri? = null
    var contentUri: Uri? = null
    var previewStartTime: String? = null
    var previewEndTime: String? = null

    fun setAdult(isAdult: Boolean) {
        _isAdultLiveData.postValue(isAdult)
    }

    fun setAvailableComment(isAvailableComment: Boolean) {
        _isAvailableCommentLiveData.postValue(isAvailableComment)
    }

    fun setPriceFree(isPriceFree: Boolean) {
        _isPriceFreeLiveData.postValue(isPriceFree)

        if (isPriceFree) {
            _purchaseOptionLiveData.postValue(PurchaseOption.BOTH)
            _isShowConfigLimitedLiveData.postValue(false)
            _isLimitedLiveData.postValue(false)
            limited = 0
            _isGeneratePreviewLiveData.postValue(true)
        } else {
            if (_purchaseOptionLiveData.value!! != PurchaseOption.RENT_ONLY) {
                _isShowConfigLimitedLiveData.postValue(true)
            }
        }
    }

    fun setGeneratePreview(isGeneratePreview: Boolean) {
        _isGeneratePreviewLiveData.value = isGeneratePreview
    }

    fun setLimited(isLimited: Boolean) {
        _isLimitedLiveData.value = isLimited

        if (!isLimited) {
            limited = 0
        }
    }

    fun setPurchaseOption(purchaseOption: PurchaseOption) {
        _purchaseOptionLiveData.postValue(purchaseOption)

        if (purchaseOption == PurchaseOption.RENT_ONLY) {
            _isShowConfigLimitedLiveData.postValue(false)
            _isLimitedLiveData.postValue(false)
            limited = 0
        } else {
            if (!_isPriceFreeLiveData.value!!) {
                _isShowConfigLimitedLiveData.postValue(true)
            }
        }
    }

    fun setActiveReservation(isActiveReservation: Boolean) {
        _isActiveReservationLiveData.postValue(isActiveReservation)
    }

    fun uploadAudioContent(onSuccess: () -> Unit) {
        if (!_isLoading.value!! && validateData()) {
            _isLoading.postValue(true)

            val isGeneratePreview = _isGeneratePreviewLiveData.value!!

            val request = CreateAudioContentRequest(
                title = title,
                detail = detail,
                tags = tags,
                price = price,
                purchaseOption = _purchaseOptionLiveData.value!!,
                limited = if (
                    price > 0 &&
                    limited > 0 &&
                    _isLimitedLiveData.value!! &&
                    _isShowConfigLimitedLiveData.value!! &&
                    !_isPriceFreeLiveData.value!!
                ) {
                    limited
                } else {
                    null
                },
                releaseDate = if (_isActiveReservationLiveData.value!!) {
                    "$releaseDate $releaseTime"
                } else {
                    null
                },
                timezone = TimeZone.getDefault().id,
                themeId = theme!!.id,
                isAdult = _isAdultLiveData.value!!,
                isGeneratePreview = isGeneratePreview,
                isCommentAvailable = _isAvailableCommentLiveData.value!!,
                previewStartTime = if (isGeneratePreview) {
                    previewStartTime
                } else {
                    null
                },
                previewEndTime = if (isGeneratePreview) {
                    previewEndTime
                } else {
                    null
                }
            )

            Logger.e("test - $request")

            val requestJson = Gson().toJson(request)

            val coverImage = if (coverImageUri != null) {
                val file = File(getRealPathFromURI(coverImageUri!!))
                MultipartBody.Part.createFormData(
                    "coverImage",
                    file.name,
                    body = object : RequestBody() {
                        override fun contentType(): MediaType {
                            return "image/*".toMediaType()
                        }

                        override fun writeTo(sink: BufferedSink) {
                            file.inputStream().use { inputStream ->
                                val buffer = ByteArray(1024)
                                var bytesRead: Int
                                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                                    sink.write(buffer, 0, bytesRead)
                                }
                            }
                        }

                        override fun contentLength(): Long {
                            return file.length()
                        }
                    }
                )
            } else {
                null
            }

            val contentFile = if (contentUri != null) {
                val file = File(getRealPathFromURI(contentUri!!))
                MultipartBody.Part.createFormData(
                    "contentFile",
                    file.name,
                    body = object : RequestBody() {
                        override fun contentType(): MediaType {
                            return "audio/*".toMediaType()
                        }

                        override fun writeTo(sink: BufferedSink) {
                            file.inputStream().use { inputStream ->
                                val buffer = ByteArray(512)
                                var bytesRead: Int
                                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                                    sink.write(buffer, 0, bytesRead)
                                }
                            }
                        }

                        override fun contentLength(): Long {
                            return file.length()
                        }
                    }
                )
            } else {
                null
            }

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

            if (contentFile == null) {
                _toastLiveData.postValue("오디오 콘텐츠를 선택해 주세요.")
                return
            }

            compositeDisposable.add(
                repository.uploadAudioContent(
                    coverImage = coverImage,
                    contentFile = contentFile,
                    request = requestJson.toRequestBody("text/plain".toMediaType()),
                    token = "Bearer ${SharedPreferenceManager.token}"
                )
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                        {
                            if (it.success && it.data != null) {
                                onSuccess()
                            } 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(
                                "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                            )
                        }
                    )
            )
        }
    }

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

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

        if (theme == null) {
            _toastLiveData.postValue("테마를 선택해 주세요.")
            return false
        }

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

        if (previewStartTime != null && previewEndTime != null) {
            val startTimeArray = previewStartTime!!.split(":")
            if (startTimeArray.size != 3) {
                _toastLiveData.postValue("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
                return false
            }

            for (time in startTimeArray) {
                if (time.length != 2) {
                    _toastLiveData.postValue("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
                    return false
                }
            }

            val endTimeArray = previewEndTime!!.split(":")
            if (endTimeArray.size != 3) {
                _toastLiveData.postValue("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
                return false
            }

            for (time in endTimeArray) {
                if (time.length != 2) {
                    _toastLiveData.postValue("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
                    return false
                }
            }

            val timeDifference = timeDifference(previewStartTime!!, previewEndTime!!)

            if (timeDifference < 15000) {
                _toastLiveData.postValue(
                    "미리 듣기의 최소 시간은 15초 입니다."
                )

                return false
            }
        } else {
            if (previewStartTime != null || previewEndTime != null) {
                _toastLiveData.postValue(
                    "미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다."
                )

                return false
            }
        }

        if (contentUri == null) {
            _toastLiveData.postValue("오디오 콘텐츠를 선택해 주세요.")
            return false
        }

        if (!isPriceFreeLiveData.value!! && price < 5) {
            _toastLiveData.postValue("콘텐츠의 최소금액은 5캔 입니다.")
            return false
        }

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

        return true
    }

    private fun timeDifference(startTime: String, endTime: String): Long {
        try {
            // Define a date format for parsing the times
            val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.KOREAN)

            // Parse the input times into Date objects
            val date1 = dateFormat.parse(startTime)
            val date2 = dateFormat.parse(endTime)

            // Check if either date is null
            if (date1 == null || date2 == null) {
                return 0
            }

            // Calculate the absolute time difference in milliseconds

            // Check if the time difference is greater than 30 seconds (30000 milliseconds)
            return date2.time - date1.time
        } catch (e: Exception) {
            // Handle invalid time formats or parsing errors
            return 0
        }
    }

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

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