package org.pixeldroid.app.profile

import android.app.Application
import android.net.Uri
import android.provider.OpenableColumns
import android.text.Editable
import android.util.Log
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import okhttp3.MultipartBody
import org.pixeldroid.app.postCreation.ProgressRequestBody
import org.pixeldroid.app.posts.fromHtml
import org.pixeldroid.app.utils.PixelDroidApplication
import org.pixeldroid.app.utils.api.objects.Account
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import javax.inject.Inject

class EditProfileViewModel(application: Application) : AndroidViewModel(application) {

    @Inject
    lateinit var apiHolder: PixelfedAPIHolder

    private val _uiState = MutableStateFlow(EditProfileActivityUiState())
    val uiState: StateFlow<EditProfileActivityUiState> = _uiState

    var oldProfile: Account? = null

    init {
        (application as PixelDroidApplication).getAppComponent().inject(this)
        loadProfile()
    }

    private fun loadProfile() {
        viewModelScope.launch {
            val api = apiHolder.api ?: apiHolder.setToCurrentUser()
            try {
                val profile = api.verifyCredentials()
                if (oldProfile == null) oldProfile = profile
                _uiState.update { currentUiState ->
                    currentUiState.copy(
                        name = oldProfile?.display_name,
                        bio = oldProfile?.source?.note,
                        profilePictureUri = oldProfile?.anyAvatar()?.toUri(),
                        privateAccount = oldProfile?.locked,
                        loadingProfile = false,
                        sendingProfile = false,
                        profileLoaded = true,
                        error = false
                    )
                }
            } catch (exception: Exception) {
                _uiState.update { currentUiState ->
                    currentUiState.copy(
                        sendingProfile = false,
                        profileSent = false,
                        loadingProfile = false,
                        profileLoaded = false,
                        error = true
                    )
                }
            }
        }
    }

    fun sendProfile() {
        val api = apiHolder.api ?: apiHolder.setToCurrentUser()

        val requestBody =
            null //MultipartBody.Part.createFormData("avatar", System.currentTimeMillis().toString(), avatarBody)

        _uiState.update { currentUiState ->
            currentUiState.copy(
                sendingProfile = true,
                profileSent = false,
                loadingProfile = false,
                profileLoaded = false,
                error = false
            )
        }

        viewModelScope.launch {
            with(uiState.value) {
                try {
                    val account = api.updateCredentials(
                        displayName = name,
                        note = bio,
                        locked = privateAccount,
                    )
                    oldProfile = account
                    _uiState.update { currentUiState ->
                        currentUiState.copy(
                            bio = account.source?.note ?: account.note?.let {fromHtml(it).toString()},
                            name = account.display_name,
                            profilePictureUri = account.anyAvatar()?.toUri(),
                            privateAccount = account.locked,
                            sendingProfile = false,
                            profileSent = true,
                            loadingProfile = false,
                            profileLoaded = true,
                            error = false
                        )
                    }
                } catch (exception: Exception) {
                    Log.e("TAG", exception.toString())
                    _uiState.update { currentUiState ->
                        currentUiState.copy(
                            sendingProfile = false,
                            profileSent = false,
                            loadingProfile = false,
                            profileLoaded = false,
                            error = true
                        )
                    }
                }
            }
        }
    }

    fun updateBio(bio: Editable?) {
        _uiState.update { currentUiState ->
            currentUiState.copy(bio = bio.toString())
        }
    }

    fun updateName(name: Editable?) {
        _uiState.update { currentUiState ->
            currentUiState.copy(name = name.toString())
        }
    }

    fun updatePrivate(isChecked: Boolean) {
        _uiState.update { currentUiState ->
            currentUiState.copy(privateAccount = isChecked)
        }
    }

    fun changesApplied() {
        _uiState.update { currentUiState ->
            currentUiState.copy(profileLoaded = false)
        }
    }

    fun madeChanges(): Boolean =
        with(uiState.value) {
            val bioUnchanged: Boolean = oldProfile?.source?.note?.let { it != bio }
                // If source note is null, check note
                ?: oldProfile?.note?.let { fromHtml(it).toString() != bio }
                ?: true
            oldProfile?.locked != privateAccount || oldProfile?.display_name != name
                    || bioUnchanged
        }

    fun clickedCard() {
        if (uiState.value.error) {
            if (!uiState.value.profileLoaded) {
                // Load failed
                loadProfile()
            } else if (uiState.value.profileLoaded) {
                // Send failed
                sendProfile()
            }
        } else {
            // Dismiss success card
            _uiState.update { currentUiState ->
                currentUiState.copy(profileSent = false)
            }
        }
    }

    fun uploadImage(image: String) {
        //TODO fix
        val inputStream =
            getApplication<PixelDroidApplication>().contentResolver.openInputStream(image.toUri())
                ?: return

        val size: Long =
            if (image.toUri().scheme == "content") {
                getApplication<PixelDroidApplication>().contentResolver.query(
                    image.toUri(),
                    null,
                    null,
                    null,
                    null
                )
                    ?.use { cursor ->
                        /* Get the column indexes of the data in the Cursor,
                                         * move to the first row in the Cursor, get the data,
                                         * and display it.
                                         */
                        val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
                        cursor.moveToFirst()
                        cursor.getLong(sizeIndex)
                    } ?: 0
            } else {
                image.toUri().toFile().length()
            }

        val imagePart = ProgressRequestBody(inputStream, size, "image/*")

        val requestBody = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("avatar", System.currentTimeMillis().toString(), imagePart)
            .build()
        val sub = imagePart.progressSubject
            .subscribeOn(Schedulers.io())
            .subscribe { percentage ->
                _uiState.update { currentUiState ->
                    currentUiState.copy(
                        uploadProgress = percentage.toInt()
                    )
                }
            }

        var postSub: Disposable? = null

        val api = apiHolder.api ?: apiHolder.setToCurrentUser()
        val inter = api.updateProfilePicture(requestBody.parts[0])

        postSub = inter
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                { it: Account ->
                    Log.e("qsdfqsdfs", it.toString())

                },
                { e: Throwable ->
                    _uiState.update { currentUiState ->
                        currentUiState.copy(
                            uploadProgress = 0,
                            uploadingPicture = true,
                            error = true
                        )
                    }
                    e.printStackTrace()
                    postSub?.dispose()
                    sub.dispose()
                },
                {
                    _uiState.update { currentUiState ->
                        currentUiState.copy(
                            uploadProgress = 100,
                            uploadingPicture = false
                        )
                    }
                    postSub?.dispose()
                    sub.dispose()
                }
            )
    }
}


data class EditProfileActivityUiState(
    val name: String? = null,
    val bio: String? = null,
    val profilePictureUri: Uri?= null,
    val privateAccount: Boolean? = null,
    val loadingProfile: Boolean = true,
    val profileLoaded: Boolean = false,
    val sendingProfile: Boolean = false,
    val profileSent: Boolean = false,
    val error: Boolean = false,
    val uploadingPicture: Boolean = false,
    val uploadProgress: Int = 0,
)

class EditProfileViewModelFactory(val application: Application) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return modelClass.getConstructor(Application::class.java).newInstance(application)
    }
}