package com.radiola.data.remote

import com.radiola.data.remote.dto.SubmitCoverDto
import com.radiola.domain.model.Track
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import java.util.Collections
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Клиентское обогащение обложек. Серверный IP забанен Apple (429), поэтому
 * iTunes-поиск делаем С УСТРОЙСТВА пользователя (его IP не забанен), а найденную
 * ссылку на арт шлём на наш бэкенд — он скачивает её и кладёт WebP к себе.
 * Дальше обложка приходит ВСЕМ через /now-playing.
 *
 * Две дорожки: приоритетная (трек, который слушают прямо сейчас — обрабатывается
 * первой) и общая (остальные now-playing). Дедуп + троттлинг, чтобы не
 * злоупотреблять iTunes с устройства.
 */
@Singleton
class CoverEnrichmentManager @Inject constructor(
    private val itunesApi: ItunesApi,
    private val radiolaApi: RadiolaApi,
) {
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    // Что уже поставлено в общую очередь (чтобы не дублировать пачку now-playing).
    private val enqueued = Collections.synchronizedSet(HashSet<String>())
    // Что уже обработали (чтобы приоритет и общая дорожка не делали двойную работу).
    private val processed = Collections.synchronizedSet(HashSet<String>())

    private val priority = Channel<Track>(Channel.UNLIMITED)
    private val normal = Channel<Track>(Channel.UNLIMITED)

    init {
        scope.launch {
            while (true) {
                val track = priority.tryReceive().getOrNull() ?: select {
                    priority.onReceive { it }
                    normal.onReceive { it }
                }
                processOne(track)
                delay(THROTTLE_MS)
            }
        }
    }

    /** Поставить пачку now-playing-треков без обложки в общую очередь. */
    fun enqueue(tracks: Collection<Track>) {
        for (t in tracks) {
            if (!isEnrichable(t)) continue
            if (enqueued.add(normKey(t))) normal.trySend(t)
        }
    }

    /** Трек, который слушают прямо сейчас — вперёд очереди (вызывать при смене трека). */
    fun enqueuePriority(track: Track?) {
        if (track == null || !isEnrichable(track)) return
        priority.trySend(track)
    }

    private fun isEnrichable(t: Track): Boolean =
        t.coverUrl.isNullOrBlank() && t.artist.isNotBlank() && t.song.isNotBlank()

    private suspend fun processOne(track: Track) {
        val key = normKey(track)
        if (!processed.add(key)) return // уже обрабатывали (другая дорожка)
        try {
            val term = clean("${track.artist} ${track.song}")
            if (term.isBlank()) return
            val art = itunesApi.search(term).results.firstOrNull()?.artworkUrl100 ?: return
            // 100x100 → 600x600 (источник покрупнее, сервер всё равно ресайзит)
            val big = art.replace(Regex("/\\d+x\\d+bb\\."), "/600x600bb.")
            val resp = radiolaApi.submitCover(SubmitCoverDto(track.artist, track.song, big))
            android.util.Log.d("CoverEnrich", "submit '${track.artist} - ${track.song}' -> ${resp.coverUrl}")
        } catch (_: Exception) {
            // сеть/429/таймаут — не критично; снимаем метку, чтобы могли попробовать позже
            processed.remove(key)
        }
    }

    private fun normKey(t: Track): String =
        "${t.artist.trim().lowercase()}|${t.song.trim().lowercase()}"

    /** Убираем суффиксы «(Original Mix)», «[... Dub]» и пунктуацию — лучше матчит. */
    private fun clean(s: String): String = s
        .replace(Regex("\\([^)]*\\)|\\[[^\\]]*\\]"), " ")
        .replace(Regex("[^\\p{L}\\p{N}]+"), " ")
        .replace(Regex("\\s+"), " ")
        .trim()

    companion object {
        private const val THROTTLE_MS = 800L
    }
}
