import APIClient
import Foundation

actor TimeSyncedCommentsCache {
    private static let maxCacheCount: Int = 10
    private static let fetchWindowSize: Int = 60

    private var commentsCache: [String: [ClipComment]] = [:]
    // Track simple time ranges that have been fetched
    private var fetchedTimeRanges: [String: [ClosedRange<Int>]] = [:]
    private var mostRecentCommentTrackerIndex: Int = .zero
    private var mostRecentCommentsSections = Array(repeating: "", count: TimeSyncedCommentsCache.maxCacheCount)

    func getCurrentCommentsCache() async -> [ClipComment] {
        var cacheCopy: [ClipComment] = []
        for (_, comments) in commentsCache {
            cacheCopy.append(contentsOf: comments)
        }
        return cacheCopy
    }

    func getCommentsForClip(_ clipRemoteID: String) async -> [ClipComment]? {
        return commentsCache[clipRemoteID]
    }

    func shouldFetchForTime(_ clipRemoteID: String, targetTime: Int) async -> Bool {
        let ranges = fetchedTimeRanges[clipRemoteID] ?? []
        let windowSize = Self.fetchWindowSize

        let targetEnd = targetTime + windowSize
        let isAlreadyFetched = ranges.contains { range in
            targetTime >= range.lowerBound && targetEnd <= range.upperBound
        }
        return !isAlreadyFetched
    }

    func markTimeRangeFetched(_ clipRemoteID: String, targetTime: Int) async {
        let windowSize = Self.fetchWindowSize
        let newRange = targetTime ... (targetTime + windowSize)

        var ranges = fetchedTimeRanges[clipRemoteID] ?? []
        ranges.append(newRange)

        // Merge overlapping ranges for efficiency
        let mergedRanges = mergeOverlappingRanges(ranges)
        fetchedTimeRanges[clipRemoteID] = mergedRanges
    }

    func updateCacheWithComments(_ clipRemoteID: String, comments: [ClipComment]) async {
        limitCache(clipRemoteID)

        let existing = commentsCache[clipRemoteID] ?? []
        let existingIds = Set(existing.map(\.id))
        let newComments = comments.filter { !existingIds.contains($0.id) }

        // Merge and sort by timestamp for better performance
        let allComments = (existing + newComments).sorted { $0.trackTimestamp < $1.trackTimestamp }
        commentsCache[clipRemoteID] = allComments
    }

    func clearCache() async {
        commentsCache.removeAll()
        fetchedTimeRanges.removeAll()
        mostRecentCommentTrackerIndex = .zero
        mostRecentCommentsSections = Array(repeating: "", count: TimeSyncedCommentsCache.maxCacheCount)
    }

    private func mergeOverlappingRanges(_ ranges: [ClosedRange<Int>]) -> [ClosedRange<Int>] {
        guard !ranges.isEmpty else { return [] }

        let sortedRanges = ranges.sorted { $0.lowerBound < $1.lowerBound }
        var merged: [ClosedRange<Int>] = [sortedRanges[0]]

        for range in sortedRanges.dropFirst() {
            let lastMerged = merged[merged.count - 1]
            if range.lowerBound <= lastMerged.upperBound + 1 {
                // Overlapping or adjacent ranges - merge them
                let newRange = lastMerged.lowerBound ... max(lastMerged.upperBound, range.upperBound)
                merged[merged.count - 1] = newRange
            } else {
                // Non-overlapping range - add it
                merged.append(range)
            }
        }
        return merged
    }

    private func limitCache(_ newClipID: String) {
        let trackerIndex = mostRecentCommentTrackerIndex
        let oldClipID = mostRecentCommentsSections[trackerIndex]

        if !oldClipID.isEmpty,
           oldClipID != newClipID,
           !mostRecentCommentsSections.contains(where: { $0 == oldClipID })
        {
            commentsCache.removeValue(forKey: oldClipID)
            fetchedTimeRanges.removeValue(forKey: oldClipID)
        }
        mostRecentCommentsSections[trackerIndex] = newClipID
        mostRecentCommentTrackerIndex = (trackerIndex + 1) % TimeSyncedCommentsCache.maxCacheCount
    }
}
