import APIClient
import ComponentLibrary
import ComposableArchitecture
import EventBusClient
import LyricsClient
import MediaPlayer
import PlayerClient
import StatsigClient
import Utilities

@Reducer
public struct LyricsReducer {
    @ObservableState
    public struct State: Equatable {
        public var clip: Clip
        public var lyrics: Lyrics
        public var totalTime: CMTime = .zero

        init(clip: Clip) {
            self.clip = clip
            self.lyrics = Lyrics(timedLyrics: clip.promptToTimedLyrics())
        }
    }

    public enum Action {
        case setup
        case getAlignedLyrics
        case updateSeconds(TimeInterval)
        case startPollingForLyricsStatus
        case lyricsEvent(LyricsClient.LyricsClientEvent)
    }

    @Dependency(APIClient.self) var apiClient
    @Dependency(\.continuousClock) private var clock
    @Dependency(LyricsClient.self) var lyricsClient
    @Dependency(\.lyricsClient.stream) var lyricsEventStream

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            struct LyricsEventStreamCancellable: Hashable {}

            switch action {
            case .setup:
                return .stream(
                    lyricsEventStream(),
                    send: Action.lyricsEvent,
                    cancellableId: LyricsEventStreamCancellable()
                )

            case .lyricsEvent(let event):
                switch event {
                case .updateLyricsOnClip(let clip, let lyrics):
                    guard clip.id == state.clip.id else { return .none } /* Avoids Race Condition */
                    state.lyrics = lyrics

                case .updateLyricsTime(let clip, let time):
                    guard clip.id == state.clip.id else { return .none } /* Avoids Race Condition */
                    state.lyrics.lyricTime = time
                }
                return .none

            case .getAlignedLyrics:
                lyricsClient.triggerSingleLyricsRequest(state.clip, state.totalTime)
                return .none

            case .updateSeconds(let seconds):
                lyricsClient.updateLyricsTime(state.clip, seconds)
                return .none

            case .startPollingForLyricsStatus:
                lyricsClient.pollAlignedLyricsFor(state.clip, state.totalTime)
                return .none
            }
        }
    }
}
