package com.prodhack.moscow2025.presentation.screens.profile

import android.content.ContentUris
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.provider.MediaStore
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import androidx.paging.map
import coil.ImageLoader
import coil.request.ImageRequest
import com.prodhack.moscow2025.data.data_providers.PhoneNumberPatternsProvider
import com.prodhack.moscow2025.domain.interfaces.GalleryRepository
import com.prodhack.moscow2025.domain.models.AuthField
import com.prodhack.moscow2025.domain.models.UpdateUserData
import com.prodhack.moscow2025.domain.usecase.GetDefaultPhoneNumberPatternUseCase
import com.prodhack.moscow2025.domain.usecase.auth.GetUserUseCase
import com.prodhack.moscow2025.domain.usecase.auth.LogOutUseCase
import com.prodhack.moscow2025.domain.usecase.auth.UpdateUserUseCase
import com.prodhack.moscow2025.domain.usecase.auth.ValidateFieldsUseCase
import com.prodhack.moscow2025.presentation.screens.fillProfile.UIPhoneNumberPattern
import com.prodhack.moscow2025.presentation.screens.fillProfile.mapToUI
import com.prodhack.moscow2025.presentation.utils.UIState
import com.prodhack.moscow2025.presentation.utils.base.BaseViewModel
import com.prodhack.moscow2025.presentation.utils.convertNumberToPattern
import com.prodhack.moscow2025.presentation.utils.toByteArray
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.koin.android.annotation.KoinViewModel

data class ProfileFormState(
	val email: String = "",
	val firstName: String = "",
	val lastName: String = "",
	val phone: String = "",
	val avatar: ByteArray? = null,
	val errors: Map<AuthField, String> = emptyMap()
) {
	override fun equals(other: Any?): Boolean {
		if (this === other) return true
		if (javaClass != other?.javaClass) return false

		other as ProfileFormState

		if (email != other.email) return false
		if (firstName != other.firstName) return false
		if (lastName != other.lastName) return false
		if (phone != other.phone) return false
		if (!avatar.contentEquals(other.avatar)) return false
		if (errors != other.errors) return false

		return true
	}

	override fun hashCode(): Int {
		var result = email.hashCode()
		result = 31 * result + firstName.hashCode()
		result = 31 * result + lastName.hashCode()
		result = 31 * result + phone.hashCode()
		result = 31 * result + (avatar?.contentHashCode() ?: 0)
		result = 31 * result + errors.hashCode()
		return result
	}
}

@KoinViewModel
class ProfileScreenViewModel(
	private val getUserUseCase: GetUserUseCase,
	private val updateUserUseCase: UpdateUserUseCase,
	private val validateFieldsUseCase: ValidateFieldsUseCase,
	private val logOutUseCase: LogOutUseCase,
	private val getDefaultPhoneNumberPatternUseCase: GetDefaultPhoneNumberPatternUseCase,
	galleryRepository: GalleryRepository
) : BaseViewModel() {
	private val _formStateProfile = MutableStateFlow(ProfileFormState())
	val formStateFillProfile: StateFlow<ProfileFormState> = _formStateProfile

	private val _profileState = MutableUIStateFlow<String>()
	val profileState: StateFlow<UIState<String>> = _profileState

	val chosenPattern = mutableStateOf<UIPhoneNumberPattern?>(null)
	val phoneNumberPatterns = mutableStateListOf<UIPhoneNumberPattern>()

	private val realState = MutableStateFlow(_formStateProfile.value)

	val madeChanges = _formStateProfile.combine(realState) { current, real ->
		current.phone != real.phone ||
				current.firstName != real.firstName ||
				current.lastName != real.lastName ||
				current.email != real.email
	}.stateIn(viewModelScope, SharingStarted.Lazily, false)

	fun reset() {
		if (madeChanges.value) {
			_formStateProfile.update {
				it.copy(
					email = realState.value.email,
					phone = realState.value.phone,
					firstName = realState.value.firstName,
					lastName = realState.value.lastName,
				)
			}
		}
	}

	fun onEmailChange(value: String) {
		_formStateProfile.update {
			it.copy(
				email = value,
				errors = it.errors - AuthField.Email
			)
		}
	}

	fun onFirstNameChange(value: String) {
		_formStateProfile.update {
			it.copy(
				firstName = value,
				errors = it.errors - AuthField.FirstName
			)
		}
	}

	fun onLastNameChange(value: String) {
		_formStateProfile.update {
			it.copy(
				lastName = value,
				errors = it.errors - AuthField.LastName
			)
		}
	}

	fun onPhoneChange(value: String) {
		val maxDigits = chosenPattern.value?.pattern?.count { it == '0' } ?: Int.MAX_VALUE
		val digits = value.filter { it.isDigit() }.take(maxDigits)
		_formStateProfile.update {
			it.copy(
				phone = digits,
				errors = it.errors - AuthField.Phone
			)
		}
	}

	val galleryItems = galleryRepository.getImagesIds().map {
		it.map { id ->
			ContentUris.withAppendedId(
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				id
			)
		}
	}

	fun post(context: Context) {
		viewModelScope.launch {
			post(
				(ImageLoader(context).execute(
					ImageRequest.Builder(context)
						.data(currentPhoto).build()
				).drawable as BitmapDrawable).bitmap
			)
		}
	}

	fun post(bitmap: Bitmap) {
		viewModelScope.launch {
			_formStateProfile.update {
				it.copy(
					avatar = bitmap.toByteArray()
				)
			}
		}
	}

	fun clearAvatar() {
		viewModelScope.launch {
			_formStateProfile.update {
				it.copy(
					avatar = null
				)
			}
		}
	}

	var currentPhoto: Uri? = null

	fun selectImage(photo: Uri) {
		currentPhoto = photo
	}

	fun submit() {
		viewModelScope.launch {
			val pattern = chosenPattern.value
			val validation = validateFieldsUseCase.validateProfile(
				chosenPattern = pattern?.mapToDomain(),
				firstName = _formStateProfile.value.firstName,
				lastName = _formStateProfile.value.lastName,
				email = _formStateProfile.value.email,
				phone = _formStateProfile.value.phone
			)

			val errors = validation.errors.toMutableMap()


			if (errors.isNotEmpty()) {
				_formStateProfile.update { it.copy(errors = errors) }
				return@launch
			}

			_profileState.emit(UIState.Loading())

			val formattedPhone = pattern?.mapToDomain()?.let { phonePattern ->
				convertNumberToPattern(phonePattern, _formStateProfile.value.phone)
			} ?: _formStateProfile.value.phone

			val result = updateUserUseCase(
				UpdateUserData(
					firstName = _formStateProfile.value.firstName,
					lastName = _formStateProfile.value.lastName,
					email = _formStateProfile.value.email,
					phone = formattedPhone
				)
			)
			result.map { it.id }.collectRequest(_profileState)
			update()
		}
	}

	fun logout() {
		viewModelScope.launch {
			logOutUseCase()
		}
	}

	fun update() {
		viewModelScope.launch {
			loadPhonePatterns()

			val user = getUserUseCase().getOrNull()
			if (user != null) {
				val digits = user.phone.orEmpty().filter { it.isDigit() }
				val selectedPattern = phoneNumberPatterns.firstOrNull { pattern ->
					val codeDigits = pattern.countryCode.filter { it.isDigit() }
					digits.startsWith(codeDigits) && digits.length >= codeDigits.length
				} ?: getDefaultPhoneNumberPatternUseCase.execute()?.mapToUI()
				?: phoneNumberPatterns.firstOrNull()

				selectedPattern?.let { chosenPattern.value = it }

				val digitsWithoutCode = selectedPattern?.let {
					val codeDigits = it.countryCode.filter { d -> d.isDigit() }
					if (digits.startsWith(codeDigits)) digits.drop(codeDigits.length) else digits
				} ?: digits

				val maxDigits = selectedPattern?.pattern?.count { it == '0' } ?: Int.MAX_VALUE

				_formStateProfile.update {
					it.copy(
						firstName = user.firstName.orEmpty(),
						lastName = user.lastName.orEmpty(),
						email = user.email,
						phone = digitsWithoutCode.take(maxDigits)
					)
				}
				realState.emit(_formStateProfile.value)
			}
		}
	}

	init {
		update()
	}

	private fun loadPhonePatterns() {
		phoneNumberPatterns.clear()
		phoneNumberPatterns.addAll(
			PhoneNumberPatternsProvider.phoneNumberPatterns.map { it.mapToUI() }
		)
		if (chosenPattern.value == null) {
			val defaultPattern = getDefaultPhoneNumberPatternUseCase.execute()?.mapToUI()
			chosenPattern.value = defaultPattern ?: phoneNumberPatterns.firstOrNull()
		}
	}
}
