import APIClient
import ComponentLibrary
import Foundation
import Waveform

/// Caches the lyrics and waveform for a clip
actor LyricsCache {
    private static let maxCacheCount: Int = 10

    private var lyricsMap: [Clip.ID: LyricsDataV2] = [:]
    private var waveformMap: [Clip.ID: WaveformData] = [:]

    // Use Least Recently Used (LRU) to maintain insertion order with accessOrder
    private var accessOrder: [Clip.ID] = []

    // MARK: - Get lyrics and waveform

    func getLyrics(for clip: Clip) -> LyricsDataV2? {
        if let lyrics = lyricsMap[clip.id] {
            moveToEnd(clip.id)
            return lyrics
        } else {
            return nil
        }
    }

    func getWaveform(for clip: Clip) -> WaveformData? {
        if let waveform = waveformMap[clip.id] {
            moveToEnd(clip.id)
            return waveform
        } else {
            return nil
        }
    }

    func getLyricsAndWaveform(for clip: Clip) -> (lyrics: LyricsDataV2?, waveform: WaveformData?) {
        let lyrics = lyricsMap[clip.id]
        let waveform = waveformMap[clip.id]

        if lyrics != nil || waveform != nil {
            moveToEnd(clip.id)
        }

        return (lyrics, waveform)
    }

    // MARK: - Set lyrics and waveform

    func setLyrics(_ lyrics: LyricsDataV2, for clip: Clip) {
        if lyricsMap[clip.id] != nil {
            removeFromAccessOrder(clip.id)
        }

        lyricsMap[clip.id] = lyrics
        accessOrder.append(clip.id)

        if lyricsMap.count > Self.maxCacheCount {
            evictLRU()
        }
    }

    func setWaveform(_ waveform: WaveformData, for clip: Clip) {
        if waveformMap[clip.id] != nil {
            removeFromAccessOrder(clip.id)
        }

        waveformMap[clip.id] = waveform
        accessOrder.append(clip.id)

        if waveformMap.count > Self.maxCacheCount {
            evictLRU()
        }
    }

    func setLyricsAndWaveform(_ lyrics: LyricsDataV2, _ waveform: WaveformData, for clip: Clip) {
        if lyricsMap[clip.id] != nil || waveformMap[clip.id] != nil {
            removeFromAccessOrder(clip.id)
        }

        lyricsMap[clip.id] = lyrics
        waveformMap[clip.id] = waveform
        accessOrder.append(clip.id)

        if lyricsMap.count > Self.maxCacheCount {
            evictLRU()
        }
    }

    // MARK: - Clear lyrics and waveform

    func clearLyrics(for clip: Clip) {
        lyricsMap.removeValue(forKey: clip.id)
        removeFromAccessOrder(clip.id)
    }

    func clearWaveform(for clip: Clip) {
        waveformMap.removeValue(forKey: clip.id)
        removeFromAccessOrder(clip.id)
    }

    func clearAll() {
        lyricsMap.removeAll()
        waveformMap.removeAll()
        accessOrder.removeAll()
    }
}

private extension LyricsCache {
    func moveToEnd(_ clipId: Clip.ID) {
        removeFromAccessOrder(clipId)
        accessOrder.append(clipId)
    }

    func removeFromAccessOrder(_ clipId: Clip.ID) {
        accessOrder.removeAll { $0 == clipId }
    }

    func evictLRU() {
        while lyricsMap.count > Self.maxCacheCount {
            guard let oldestClipId = accessOrder.first else { break }
            lyricsMap.removeValue(forKey: oldestClipId)
            waveformMap.removeValue(forKey: oldestClipId)
            accessOrder.removeFirst()
        }
    }
}
