package de.softwareapp_hb.privateqrscanner.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import de.softwareapp_hb.privateqrscanner.AppContainer
import de.softwareapp_hb.privateqrscanner.domain.ScanResult
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

data class BatchScanRecord(
    val result: ScanResult,
    val timestamp: Long
)

data class ScannerUiState(
    val lastResult: ScanResult? = null,
    val analysisEnabled: Boolean = true,
    val lastScanTimestamp: Long = 0L,
    val batchMode: Boolean = false,
    val batchResults: List<BatchScanRecord> = emptyList(),
    val eventTicketWhitelistCount: Int = 0,
    val recentScanKeys: List<String> = emptyList(),
    val duplicateFeedbackNonce: Int = 0,
    val scanFeedbackNonce: Int = 0
)

enum class EventTicketScanDecision {
    Accept,
    Unregistered,
    DuplicateAlert,
    Ignore
}

class ScannerViewModel(
    private val saveScan: suspend (content: String, type: String) -> Unit,
    private val nowProvider: () -> Long = { System.currentTimeMillis() }
) : ViewModel() {
    private companion object {
        const val GENERAL_DEBOUNCE_MS = 800L
        const val SAME_CODE_HOLDOFF_MS = 2500L
        const val EVENT_TICKET_HOLDOFF_MS = 30_000L
    }

    private val _uiState = MutableStateFlow(ScannerUiState())
    val uiState: StateFlow<ScannerUiState> = _uiState.asStateFlow()
    private val recentScanKeySet = LinkedHashSet<String>(200)
    private val batchKeySet = LinkedHashSet<String>(100)
    private val eventTicketSeenKeys = HashSet<String>()
    private val eventTicketWhitelistIds = HashSet<String>()
    private val eventTicketDuplicateCooldowns = HashMap<String, Long>()
    private val eventTicketRecentlyAccepted = HashMap<String, Long>()
    private var lastAcceptedKey: String? = null
    private var lastAcceptedTimestamp: Long = 0L

    fun onScan(result: ScanResult) {
        val now = nowProvider()
        val current = _uiState.value
        if (!current.analysisEnabled) return

        val key = "${result.displayType}|${result.content}"
        if (now - current.lastScanTimestamp < GENERAL_DEBOUNCE_MS) return
        if (key == lastAcceptedKey && now - lastAcceptedTimestamp < SAME_CODE_HOLDOFF_MS) return

        val isDuplicate = key in recentScanKeySet
        recentScanKeySet.remove(key)
        recentScanKeySet.add(key)
        while (recentScanKeySet.size > 200) {
            recentScanKeySet.remove(recentScanKeySet.first())
        }
        val updatedRecent = recentScanKeySet.toList().asReversed()
        lastAcceptedKey = key
        lastAcceptedTimestamp = now

        _uiState.value = if (current.batchMode) {
            val updatedBatch = if (key in batchKeySet) {
                current.batchResults
            } else {
                batchKeySet.add(key)
                val nextBatch = (listOf(BatchScanRecord(result = result, timestamp = now)) + current.batchResults)
                    .take(100)
                while (batchKeySet.size > 100) {
                    batchKeySet.remove(batchKeySet.first())
                }
                nextBatch
            }
            current.copy(
                lastResult = null,
                analysisEnabled = true,
                lastScanTimestamp = now,
                batchResults = updatedBatch,
                recentScanKeys = updatedRecent,
                duplicateFeedbackNonce = if (isDuplicate) current.duplicateFeedbackNonce + 1 else current.duplicateFeedbackNonce,
                scanFeedbackNonce = current.scanFeedbackNonce + 1
            )
        } else {
            current.copy(
                lastResult = result,
                analysisEnabled = false,
                lastScanTimestamp = now,
                recentScanKeys = updatedRecent,
                duplicateFeedbackNonce = if (isDuplicate) current.duplicateFeedbackNonce + 1 else current.duplicateFeedbackNonce,
                scanFeedbackNonce = current.scanFeedbackNonce + 1
            )
        }

        viewModelScope.launch {
            saveScan(result.content, result.displayType)
        }
    }

    fun resumeScanning() {
        _uiState.value = _uiState.value.copy(analysisEnabled = true, lastResult = null)
    }

    fun setBatchMode(enabled: Boolean) {
        _uiState.value = _uiState.value.copy(
            batchMode = enabled,
            analysisEnabled = true,
            lastResult = null
        )
    }

    fun clearBatchResults() {
        batchKeySet.clear()
        eventTicketSeenKeys.clear()
        eventTicketDuplicateCooldowns.clear()
        eventTicketRecentlyAccepted.clear()
        _uiState.value = _uiState.value.copy(batchResults = emptyList())
    }

    fun auditDuplicateTicketScan(result: ScanResult) {
        viewModelScope.launch {
            saveScan(result.content, "Duplicate ticket (${result.displayType})")
        }
    }

    fun auditUnregisteredTicketScan(result: ScanResult) {
        viewModelScope.launch {
            saveScan(result.content, "Unregistered ticket (${result.displayType})")
        }
    }

    fun replaceEventTicketWhitelist(ids: Set<String>) {
        eventTicketWhitelistIds.clear()
        eventTicketWhitelistIds.addAll(ids.map(::normalizeWhitelistId).filter { it.isNotBlank() })
        _uiState.value = _uiState.value.copy(eventTicketWhitelistCount = eventTicketWhitelistIds.size)
    }

    fun evaluateEventTicketScan(result: ScanResult): EventTicketScanDecision {
        val key = "${result.displayType}|${result.content}"
        val normalizedContent = normalizeWhitelistId(result.content)
        val now = nowProvider()
        if (eventTicketWhitelistIds.isNotEmpty() && normalizedContent !in eventTicketWhitelistIds) {
            return EventTicketScanDecision.Unregistered
        }
        if (key !in eventTicketSeenKeys) {
            eventTicketSeenKeys.add(key)
            eventTicketRecentlyAccepted[key] = now
            return EventTicketScanDecision.Accept
        }

        val acceptedAt = eventTicketRecentlyAccepted[key] ?: 0L
        if (now - acceptedAt < EVENT_TICKET_HOLDOFF_MS) {
            return EventTicketScanDecision.Ignore
        }

        val cooldownUntil = eventTicketDuplicateCooldowns[key] ?: 0L
        if (now < cooldownUntil) {
            return EventTicketScanDecision.Ignore
        }

        eventTicketDuplicateCooldowns[key] = now + EVENT_TICKET_HOLDOFF_MS
        return EventTicketScanDecision.DuplicateAlert
    }

    private fun normalizeWhitelistId(value: String): String {
        return value.trim().lowercase()
    }

    class Factory(private val container: AppContainer) : ViewModelProvider.Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return ScannerViewModel(
                saveScan = { content, type -> container.scanRepository.maybeSaveScan(content, type) }
            ) as T
        }
    }
}
