import APIClient
import Combine
import ComposableArchitecture
import Foundation
import Utilities

final actor ClipLikeCache: Sendable {
    private var clipLikeStatus: [String: Bool] = [:]
    private var accessOrder: [String] = []
    private let maxCacheSize = 500
    private var inProgressLikeRequests: [String: Task<Void, Never>] = [:]

    func getClipLikeStatus(for hookId: String) -> Bool? {
        if clipLikeStatus[hookId] != nil {
            updateAccessOrder(for: hookId)
        }
        return clipLikeStatus[hookId]
    }

    func setClipLikeStatus(_ isLiked: Bool, for hookId: String) {
        clipLikeStatus[hookId] = isLiked
        updateAccessOrder(for: hookId)
        evictIfNeeded()
    }

    func hydrateFromHooks(_ hooks: [Hook]) {
        for hook in hooks {
            guard let clip = hook.clip else { continue }
            clipLikeStatus[hook.id] = clip.isLiked
            updateAccessOrder(for: hook.id)
        }
        evictIfNeeded()
    }

    func removeHook(_ hookId: String) {
        clipLikeStatus.removeValue(forKey: hookId)
        accessOrder.removeAll { $0 == hookId }
    }

    private func updateAccessOrder(for hookId: String) {
        accessOrder.removeAll { $0 == hookId }
        accessOrder.append(hookId)
    }

    private func evictIfNeeded() {
        while clipLikeStatus.count > maxCacheSize {
            guard let leastUsed = accessOrder.first else { break }
            clipLikeStatus.removeValue(forKey: leastUsed)
            accessOrder.removeFirst()
        }
    }

    func toggleClipLike(
        hook: Hook,
        eventSubject: PassthroughSubject<HooksPlayerEvent, Never>
    ) async {
        guard !hasInProgressRequest(for: hook.id) else { return }

        let currentStatus = getClipLikeStatus(for: hook.id) ?? false
        let newStatus = !currentStatus

        setClipLikeStatus(newStatus, for: hook.id)

        let updatedStatus = getClipLikeStatus(for: hook.id) ?? newStatus
        eventSubject.send(.clipLikeStatusUpdated(hook.id, updatedStatus))

        let task = Task { [weak self] in
            defer {
                Task {
                    await self?.removeInProgressRequest(for: hook.id)
                }
            }

            do {
                @Dependency(\.apiClientV2) var apiClientV2
                guard let clip = hook.clip else { return }
                try await apiClientV2.setReaction(clip, !clip.isLiked, clip.isDisliked, nil)
            } catch {
                log.telemetry.error(error, message: "Failed to toggle clip like for \(hook.id).")
                await self?.revertClipLikeStatus(
                    hookId: hook.id,
                    to: currentStatus,
                    eventSubject: eventSubject
                )
            }
        }

        inProgressLikeRequests[hook.id] = task
    }

    private func hasInProgressRequest(for hookId: String) -> Bool {
        inProgressLikeRequests[hookId] != nil
    }

    private func removeInProgressRequest(for hookId: String) {
        inProgressLikeRequests.removeValue(forKey: hookId)
    }

    private func revertClipLikeStatus(
        hookId: String,
        to status: Bool,
        eventSubject: PassthroughSubject<HooksPlayerEvent, Never>
    ) {
        setClipLikeStatus(status, for: hookId)

        let revertedStatus = getClipLikeStatus(for: hookId) ?? status
        eventSubject.send(.clipLikeStatusUpdated(hookId, revertedStatus))
    }
}
