import APIClient
import AsyncAlgorithms
import Combine
import ComposableArchitecture
import CoreMedia.CMTime
import Foundation
import Localization

@DependencyClient
public struct LyricsClient {
    public enum LyricsClientEvent {
        case updateLyricsOnClip(_ clip: Clip, _ lyrics: Lyrics)
        case updateLyricsTime(_ clip: Clip, _ time: TimeInterval?)
    }

    public var stream: () -> AsyncStream<LyricsClientEvent> = { .never }

    // Responses for requests are triggered through AsyncChannel
    public var triggerSingleLyricsRequest: (_ clip: Clip, _ givenTotalTime: CMTime) -> Void
    public var pollAlignedLyricsFor: (_ clipID: Clip, _ givenTotalTime: CMTime) -> Void
    public var updateLyricsTime: (_ clip: Clip, _ time: TimeInterval?) -> Void

    // New alignment processing
    public var triggerAlignedLyricsRequestV2: (_ clip: Clip) -> Void
}

extension LyricsClient: DependencyKey {
    public static var liveValue: LyricsClient {
        @Dependency(APIClient.self) var apiClient
        let subject = PassthroughSubject<LyricsClientEvent, Never>()

        return Self(
            stream: {
                UncheckedSendable(subject.values).eraseToStream()
            },
            triggerSingleLyricsRequest: { clip, givenTotalTime in
                Task {
                    let manager = AlignedLyricsManager()
                    await manager.loadAlignedLyricsForClip(clip, givenTotalTime: givenTotalTime)
                    let currentLyrics = manager.lyricsForClip(clip)
                    subject.send(.updateLyricsOnClip(clip, currentLyrics))

                    /* Removes old songs in cache - cache limit of 1 currently */
                    manager.updateMostRecentLyricsAndRemoveOld(clip)
                }

            },
            pollAlignedLyricsFor: { clip, givenTotalTime in
                Task(priority: .medium) { [clip, givenTotalTime] in
                    let manager = AlignedLyricsManager()
                    while manager.alignedLyricsMap.shouldContinuePolling(clip.id) {
                        await manager.loadAlignedLyricsForClip(clip, givenTotalTime: givenTotalTime)
                        var lyricsPollingInterval: TimeInterval { clip.isScene ? 3.0 : 6.0 }
                        try await Task.sleep(for: .seconds(lyricsPollingInterval))

                        let currentLyrics = manager.lyricsForClip(clip)
                        subject.send(.updateLyricsOnClip(clip, currentLyrics))
                    }
                }
            },
            updateLyricsTime: { clip, time in
                Task { [clip, time] in
                    let manager = AlignedLyricsManager()
                    manager.updateLyricsTime(clip, time: time)
                    subject.send(.updateLyricsTime(clip, time))
                }
            },
            triggerAlignedLyricsRequestV2: { _ in
            }
        )
    }
}

public extension DependencyValues {
    var lyricsClient: LyricsClient {
        get { self[LyricsClient.self] }
        set { self[LyricsClient.self] = newValue }
    }
}
