import APIClient
import ComposableArchitecture
import FeatureOmniPlayer
import OmniPlayerClient
import PlayerUtilities
import SwiftUI
import Utilities

extension HooksFeedReducer {
    func prepareOmniPlayerForPlayback(state: inout State, clip: Clip) -> Effect<Action> {
        // Pause the current hook first
        hooksPlayerClient.pauseCurrentHook(.openOmniPlayer(clipId: clip.id.remoteId))
        // Get the current hook to pass to the omniplayer
        let currentHook: Hook? = state.currentIndex < state.hooks.count ? state.hooks[state.currentIndex] : nil
        // Create shared player state for the ExpandedPlayerReducer

        let currentClip = currentHook?.clip ?? clip

        var updatedClip = currentClip
        if let hookId = currentHook?.id {
            if let correctLikeStatus = state.hooksMetadata.clipLikeStatus[hookId] {
                if updatedClip.isLiked != correctLikeStatus {
                    updatedClip.isLiked = correctLikeStatus
                }
            }

            if let correctPlayCount = state.hooksMetadata.clipPlayCount[hookId] {
                updatedClip.playCount = correctPlayCount
            }
        }

        hydrateRelationshipsForClip(updatedClip.id)

        let sharedPlayerState: Shared<OmniPlayerPlaybackState> = Shared(
            value: OmniPlayerPlaybackState(
                clip: updatedClip,
                queue: [updatedClip],
                me: state.$me,
                source: PlaybackSource.hooksFeed,
                isPlayingNewGeneration: false
            )
        )
        state.expandedState = .init(
            player: sharedPlayerState,
            me: state.$me,
            prompt: nil,
            autoStart: true,
            initialIndex: 0,
            queue: [updatedClip],
            source: .hooksFeed,
            sourceHook: currentHook
        )
        state.isShowingOmniPlayer = true
        state.$isHooksOmniPlayerVisible.withLock { $0 = true }

        guard let clip = state.expandedState?.clip else { return .none }

        return .run { [userId = state.me.user.id] _ in
            @Dependency(OmniPlayerClient.self) var omniplayerClient
            let currentQueue = await omniplayerClient.getQueue()
            // Fetch and store hook session ID to send to song session analytics
            let hookSessionId: String
            if let currentHookSessionId = await hooksPlayerClient.getCurrentSessionId() {
                hookSessionId = currentHookSessionId
            } else {
                hookSessionId = UUID().uuidString // Shouldn't happen unless we're in a bad race condition
            }
            // If the queue is empty, set up the omniplayer client
            if currentQueue.isEmpty {
                try? omniplayerClient.setup(userId)
            }
            // Play the song using a queue override while sending the referring hook session ID
            omniplayerClient.setQueueOverride(
                [clip],
                .hook(id: clip.id.remoteId, sessionId: hookSessionId),
                SessionContext(source: .hooksFeed)
            )
        }
    }

    func stopOmniPlayerImmediately(hookId: String) {
        @Dependency(OmniPlayerClient.self) var omniplayerClient
        omniplayerClient.clearQueueOverride(autoPlay: false, cause: .closeHooksFeedOmniPlayer(hookId: hookId))
        omniplayerClient.clearPlaybackConfigurationOverrides()
    }

    func handlePlaybackAfterClosingOmniPlayer(
        autoPlaySong _: Bool = false,
        autoPlayHook: Bool = true,
        hookId: String
    ) {
        stopOmniPlayerImmediately(hookId: hookId)
        guard autoPlayHook else { return }
        Task {
            @Dependency(OmniPlayerClient.self) var omniplayerClient
            guard let currentSongSessionId = await omniplayerClient.getCurrentSessionId() else { return }
            hooksPlayerClient.playCurrentHook(.closeOmniPlayer(clipId: hookId, songSessionId: currentSongSessionId))
        }
    }

    func handleClipLikedFromOutsideHooksFeed(state: inout State, clip: Clip) -> Effect<Action> {
        // Updates ONLY like status in the feed and in HookPlayerClient's SimpleLikeCache
        guard let index = state.hooks.firstIndex(where: { $0.clip?.id == clip.id }) else {
            return .none
        }
        let clipId = clip.id.remoteId
        let hookId = state.hooks[index].id

        let now = Date()
        if let lastEventTime = state.recentLikeEvents[clipId],
           now.timeIntervalSince(lastEventTime) < state.likeEventDeduplicationWindow,
           state.hooksMetadata.clipLikeStatus[hookId] == clip.isLiked
        {
            return .none
        }

        state.recentLikeEvents[clipId] = now

        state.recentLikeEvents = state.recentLikeEvents.filter {
            now.timeIntervalSince($0.value) < state.likeEventDeduplicationWindow
        }

        // Only update like status
        state.hooksMetadata.clipLikeStatus[hookId] = clip.isLiked

        // Update the hook's clip with the new like status
        if var hook = state.hooks[id: hookId] {
            hook.clip = clip
            state.hooks[id: hookId] = hook
        }

        return .run { _ in
            await hooksPlayerClient.setClipLikeStatus(clip.isLiked, hookId)
        }
    }

    func handleClipUpdatedFromOutsideHooksFeed(state: inout State, clip: Clip) -> Effect<Action> {
        // Updates play count and other clip properties (but not like status)
        guard let index = state.hooks.firstIndex(where: { $0.clip?.id == clip.id }) else {
            return .none
        }
        let hookId = state.hooks[index].id

        // Only update non-like properties
        state.hooksMetadata.clipPlayCount[hookId] = clip.playCount

        if var hook = state.hooks[id: hookId] {
            hook.clip = clip
            state.hooks[id: hookId] = hook
        }

        return .run { _ in
            await hooksPlayerClient.setClipPlayCount(clip.playCount, hookId)
        }
    }
}
