import APIClient
import ComposableArchitecture
import EventBusClient
import Foundation

@DependencyClient
public struct ClipPollingClient {
    public var pollClipsForStatusComplete: (_ clips: [Clip]) -> Void
    public var pollClipsToUnlock: (_ clips: Set<Clip>) -> Void
}

// Polls streaming clips and send ClipEvent when status is complete.
extension ClipPollingClient: DependencyKey {
    private static var pollingClips: Set<Clip> = []

    public static let liveValue = Self(
        pollClipsForStatusComplete: { clips in
            Task.detached(priority: .background) {
                @Dependency(\.apiClientV2) var api
                @Dependency(\.eventBus.sendClipEvent) var sendClipEvent

                var filteredClips = clips.filter { $0.status != .error }

                // Poll for 1 minute, or until complete, every 3 seconds
                var timeout: TimeInterval = 180
                let pollInterval: TimeInterval = 3
                let startTime = Date()

                while Date().timeIntervalSince(startTime) < timeout {
                    do {
                        let newClips = try await api.getFeedByIds(filteredClips.map { $0.id.remoteId })
                        for clip in newClips {
                            // Only update the clip if it's different from the old clip
                            guard let clipIndex = filteredClips.firstIndex(where: { $0.id == clip.id }) else { continue }
                            let oldClip = filteredClips[clipIndex]
                            let oldDuration = oldClip.duration
                            let oldStatus = oldClip.status
                            let newDuration = clip.duration
                            let newStatus = clip.status
                            guard newDuration != oldDuration || newStatus != oldStatus else { continue }
                            // Send updated clip if duration or status are different
                            sendClipEvent(.updateClip(clip))
                            filteredClips[clipIndex] = clip
                        }
                        let allComplete = newClips.allSatisfy { clip in
                            let currentClip = clips.first(where: { $0.id == clip.id }) ?? clip
                            return currentClip.status == .complete
                        }

                        if allComplete {
                            timeout = 0
                        }
                        try await Task.sleep(for: .seconds(pollInterval))
                    } catch is CancellationError {
                        return
                    } catch {
                        return
                    }
                }
            }
        },
        pollClipsToUnlock: { clips in
            let shouldStartTask = !pollingClips.isEmpty
            pollingClips = clips.filter { $0.status != .error }

            guard shouldStartTask, !pollingClips.isEmpty else { return }

            Task.detached(priority: .background) {
                @Dependency(\.apiClientV2) var api
                @Dependency(\.eventBus.sendClipEvent) var sendClipEvent

                // Poll for 3 minutes, or until complete, every 3 seconds
                var timeout: TimeInterval = 180
                let pollInterval: TimeInterval = 3
                let startTime = Date()

                while Date().timeIntervalSince(startTime) < timeout {
                    do {
                        let newClips = try await api.getFeedByIds(pollingClips.map { $0.id.remoteId })
                        for clip in newClips {
                            if clip.type == .gen {
                                sendClipEvent(.updateClip(clip))
                            }
                        }

                        pollingClips = pollingClips.filter { $0.type == .preview }
                        if pollingClips.isEmpty {
                            timeout = 0
                        }
                        try await Task.sleep(for: .seconds(pollInterval))
                    } catch is CancellationError {
                        pollingClips = []
                        return
                    } catch {
                        pollingClips = []
                        return
                    }
                }

                pollingClips = []
            }
        }
    )
}
