package app.closer.ui.settings

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Outcome
import app.closer.domain.model.OutcomeDay
import app.closer.domain.model.OutcomeDayKey
import app.closer.domain.model.OutcomeScores
import app.closer.data.local.PendingInviteStore
import app.closer.data.local.RecoveryPhraseStore
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.CoupleRepository
import app.closer.domain.repository.OutcomeRepository
import app.closer.domain.repository.SettingsRepository
import app.closer.domain.repository.ThemeMode
import app.closer.domain.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import javax.inject.Inject

data class SettingsUiState(
    val isLoading: Boolean = true,
    val displayName: String = "",
    val photoUrl: String = "",
    val email: String = "",
    val partnerName: String? = null,
    val partnerPhotoUrl: String? = null,
    val isPaired: Boolean = false,
    val isSigningOut: Boolean = false,
    val themeMode: ThemeMode = ThemeMode.DEVICE,
    val navigateTo: String? = null,
    val outcomeBaselineDialogDue: Boolean = false,
    val outcomeFollowUpDay: OutcomeDay? = null,
    val outcomeSubmitSuccess: Boolean = false,
    val outcomeError: String? = null,
    val unreadActivityCount: Int = 0
)

@HiltViewModel
class SettingsViewModel @Inject constructor(
    private val authRepository: AuthRepository,
    private val userRepository: UserRepository,
    private val coupleRepository: CoupleRepository,
    private val settingsRepository: SettingsRepository,
    private val outcomeRepository: OutcomeRepository,
    private val recoveryPhraseStore: RecoveryPhraseStore,
    private val pendingInviteStore: PendingInviteStore,
    private val activityDataSource: app.closer.data.remote.FirestoreActivityDataSource
) : ViewModel() {

    private val _uiState = MutableStateFlow(SettingsUiState())
    val uiState: StateFlow<SettingsUiState> = _uiState.asStateFlow()
    private var currentUserJob: Job? = null
    private var partnerUserJob: Job? = null

    init {
        loadSettings()
        observeThemeMode()
        observeUnreadActivity()
    }

    private fun observeUnreadActivity() {
        val userId = authRepository.currentUserId ?: return
        viewModelScope.launch {
            activityDataSource.observeUnreadCount(userId).collect { count ->
                _uiState.update { it.copy(unreadActivityCount = count) }
            }
        }
    }

    private fun observeThemeMode() {
        viewModelScope.launch {
            settingsRepository.settings.collect { settings ->
                _uiState.update { it.copy(themeMode = settings.themeMode) }
            }
        }
    }

    fun setThemeMode(mode: ThemeMode) {
        _uiState.update { it.copy(themeMode = mode) }
        viewModelScope.launch { settingsRepository.setThemeMode(mode) }
    }

    private fun loadSettings() {
        viewModelScope.launch {
            val userId = authRepository.currentUserId ?: run {
                _uiState.update { it.copy(isLoading = false) }
                return@launch
            }
            val email = authRepository.currentUserEmail ?: ""
            val user = runCatching { userRepository.getUser(userId) }
                .onFailure { Log.w(TAG, "Could not load current user profile", it) }
                .getOrNull()
            val couple = coupleRepository.getCoupleForUser(userId)
            val partnerId = couple?.userIds?.firstOrNull { it != userId }
            val partner = partnerId?.let {
                runCatching { userRepository.getUser(it) }
                    .onFailure { e -> Log.w(TAG, "Could not load partner profile", e) }
                    .getOrNull()
            }
            val outcomes = couple?.let {
                runCatching { outcomeRepository.getOutcomes(it.id) }
                    .onFailure { Log.w(TAG, "Could not load outcomes", it) }
                    .getOrDefault(emptyList())
            } ?: emptyList()
            val baselineShownAt = settingsRepository.settings.first().outcomeBaselineShownAt
            val outcomeBaselineDialogDue = couple != null &&
                outcomes.none { it.dayKey == OutcomeDay.BASELINE.key } &&
                baselineShownAt == 0L
            val followUpDay = couple?.let { dueFollowUpDay(it.createdAt, outcomes) }

            _uiState.update {
                it.copy(
                    isLoading = false,
                    displayName = user?.displayName ?: "",
                    photoUrl = user?.photoUrl ?: "",
                    email = email,
                    partnerName = partner?.displayName,
                    partnerPhotoUrl = partner?.photoUrl,
                    isPaired = couple != null,
                    outcomeBaselineDialogDue = outcomeBaselineDialogDue,
                    outcomeFollowUpDay = followUpDay
                )
            }
            observeCurrentUser(userId)
            observePartnerUser(partnerId)
        }
    }

    private fun observeCurrentUser(userId: String) {
        currentUserJob?.cancel()
        currentUserJob = viewModelScope.launch {
            userRepository.observeUser(userId)
                .catch { error -> Log.w(TAG, "Current user observer stopped", error) }
                .collect { user ->
                    _uiState.update {
                        it.copy(
                            displayName = user?.displayName ?: it.displayName,
                            photoUrl = user?.photoUrl ?: ""
                        )
                    }
                }
        }
    }

    private fun observePartnerUser(partnerId: String?) {
        partnerUserJob?.cancel()
        if (partnerId == null) {
            _uiState.update { it.copy(partnerName = null, partnerPhotoUrl = null) }
            return
        }
        partnerUserJob = viewModelScope.launch {
            userRepository.observeUser(partnerId)
                .catch { error -> Log.w(TAG, "Partner user observer stopped", error) }
                .collect { partner ->
                    _uiState.update {
                        it.copy(
                            partnerName = partner?.displayName,
                            partnerPhotoUrl = partner?.photoUrl
                        )
                    }
                }
        }
    }

    fun submitOutcome(dayKey: OutcomeDayKey, scores: OutcomeScores) {
        viewModelScope.launch {
            _uiState.update { it.copy(outcomeSubmitSuccess = false, outcomeError = null) }
            try {
                val userId = authRepository.currentUserId
                    ?: throw IllegalStateException("Not signed in.")
                val couple = coupleRepository.getCoupleForUser(userId)
                    ?: throw IllegalStateException("Not paired.")
                outcomeRepository.submitOutcome(couple.id, dayKey, scores).getOrThrow()
                settingsRepository.setOutcomeLastPromptedDay(dayKey)
                if (dayKey == OutcomeDay.BASELINE.key) {
                    settingsRepository.markOutcomeBaselineShown()
                }
                _uiState.update {
                    it.copy(
                        outcomeSubmitSuccess = true,
                        outcomeBaselineDialogDue = false,
                        outcomeFollowUpDay = null
                    )
                }
                loadSettings()
            } catch (e: Exception) {
                _uiState.update { it.copy(outcomeError = e.message ?: "Couldn’t save check-in.") }
            }
        }
    }

    fun markBaselineOutcomeShown() {
        viewModelScope.launch {
            settingsRepository.markOutcomeBaselineShown()
            _uiState.update { it.copy(outcomeBaselineDialogDue = false) }
        }
    }

    fun markFollowUpOutcomeShown(dayKey: OutcomeDayKey) {
        viewModelScope.launch {
            settingsRepository.setOutcomeLastPromptedDay(dayKey)
            _uiState.update { it.copy(outcomeFollowUpDay = null) }
        }
    }

    fun consumeOutcomeSuccess() = _uiState.update { it.copy(outcomeSubmitSuccess = false) }
    fun consumeOutcomeError() = _uiState.update { it.copy(outcomeError = null) }

    private fun dueFollowUpDay(createdAt: Long, outcomes: List<Outcome>): OutcomeDay? {
        val paired = LocalDate.ofInstant(Instant.ofEpochMilli(createdAt), ZoneId.systemDefault())
        val today = LocalDate.now()
        val ageDays = java.time.temporal.ChronoUnit.DAYS.between(paired, today)
        val due = listOf(
            OutcomeDay.DAY_30 to 30,
            OutcomeDay.DAY_60 to 60,
            OutcomeDay.DAY_90 to 90
        ).firstOrNull { (_, days) -> ageDays >= days } ?: return null
        return if (outcomes.any { it.dayKey == due.first.key }) null else due.first
    }

    fun signOut() {
        _uiState.update { it.copy(isSigningOut = true) }
        currentUserJob?.cancel()
        partnerUserJob?.cancel()
        viewModelScope.launch {
            authRepository.signOut()
            recoveryPhraseStore.clear()
            pendingInviteStore.clear()
            _uiState.update { it.copy(isSigningOut = false, navigateTo = AppRoute.ONBOARDING) }
        }
    }

    fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }

    companion object {
        private const val TAG = "SettingsViewModel"
    }
}
