import AVFoundation
import AVPlayerClient
import Combine
import ComposableArchitecture
import Foundation
import MediaPlayer
import PlayerUtilities
import UIKit

@DependencyClient
public struct MusicPlayerClient: PlayerEngine, Sendable {
    // Setup and teardown
    public var setup: @Sendable () async throws -> Void = { reportIssue("setup() not implemented") }
    public var teardown: @Sendable () -> Void = {}

    // Player controls
    public var play: @Sendable () -> Void = {}
    public var pause: @Sendable () -> Void = {}
    public var seek: @Sendable (CMTime) async throws -> Void = { _ in }

    // Volume controls
    public var setVolume: @Sendable (Float) -> Void = { _ in }
    public var getVolume: @Sendable () -> Float = { 1.0 }

    // Convenience methods
    public var replaceCurrentItem: @Sendable (String?) async throws -> CMTime = { _ in CMTime.zero }

    // Now playing info
    public var setupNowPlayingInfo: @Sendable (_ title: String, _ artist: String, _ artworkURL: String?, _ elapsedTime: CMTime, _ totalTime: CMTime) -> Void = { _, _, _, _, _ in }

    // Stream access
    public var timeControlStatus: @Sendable () -> AsyncStream<AVPlayer.TimeControlStatus> = { AsyncStream { _ in } }
    public var periodicTime: @Sendable () -> AsyncStream<CMTime> = { AsyncStream { $0.yield(CMTime.zero); $0.finish() } }
    public var didPlayToEndTime: @Sendable () -> AsyncStream<AVPlayerItem> = { AsyncStream { _ in } }

    // State access
    public var getCurrentItem: @Sendable () async -> AVPlayerItem? = { nil }
    public var isPlaybackLikelyToKeepUp: @Sendable () async -> Bool = { false }
    public var getUnderlyingPlayer: @Sendable () -> Any = { AVPlayer() }

    // Asset management
    public var preloadAssets: @Sendable (_ urls: [URL]) async -> Void = { _ in }
    public var cancelPreload: @Sendable () async -> Void = {}

    // Time update configuration
    public var setTimeUpdateInterval: @Sendable (TimeInterval) -> Void = { _ in }

    // Sets the current owner of AVPlayerClient (OmniPlayer, SnippetPlayer, etc.)
    public var setOwner: @Sendable (PlayerOwner) async -> Void = { _ in }
    public var getOwner: @Sendable () async -> PlayerOwner = { .none }
}

extension MusicPlayerClient: DependencyKey {
    // Store the configured engine for dependency injection
    private static var configuredPlayerEngine: PlayerEngine = AVPlayerClient()

    // Configure which player engine to use - call this at app startup
    public static func configure(with playerEngineType: PlayerEngineType) {
        switch playerEngineType {
        case .avPlayer:
            configuredPlayerEngine = AVPlayerClient()
        }
    }

    // Default to AVPlayerClient if no configuration is provided
    public static let liveValue: Self = {
        let playerEngine = configuredPlayerEngine
        return Self(
            setup: { @Sendable in
                try await playerEngine.setup()
            },
            teardown: { @Sendable in
                playerEngine.teardown()
            },
            play: { @Sendable in
                playerEngine.play()
            },
            pause: { @Sendable in
                playerEngine.pause()
            },
            seek: { @Sendable time in
                try await playerEngine.seek(time)
            },
            setVolume: { @Sendable volume in
                playerEngine.setVolume(volume)
            },
            getVolume: { @Sendable in
                playerEngine.getVolume()
            },
            replaceCurrentItem: { @Sendable url in
                return try await playerEngine.replaceCurrentItem(url)
            },
            setupNowPlayingInfo: { @Sendable title, artist, artworkURL, elapsedTime, totalTime in
                playerEngine.setupNowPlayingInfo(title, artist, artworkURL, elapsedTime, totalTime)
            },
            timeControlStatus: { @Sendable in
                playerEngine.timeControlStatus()
            },
            periodicTime: { @Sendable in
                playerEngine.periodicTime()
            },
            didPlayToEndTime: { @Sendable in
                playerEngine.didPlayToEndTime()
            },
            getCurrentItem: { @Sendable in
                await playerEngine.getCurrentItem()
            },
            isPlaybackLikelyToKeepUp: { @Sendable in
                await playerEngine.isPlaybackLikelyToKeepUp()
            },
            getUnderlyingPlayer: { @Sendable in
                playerEngine.getUnderlyingPlayer()
            },
            preloadAssets: { @Sendable urls in
                await playerEngine.preloadAssets(urls)
            },
            cancelPreload: { @Sendable in
                await playerEngine.cancelPreload()
            },
            setTimeUpdateInterval: { @Sendable interval in
                playerEngine.setTimeUpdateInterval(interval)
            },
            setOwner: { @Sendable owner in
                await playerEngine.setOwner(owner)
            },
            getOwner: { @Sendable in
                await playerEngine.getOwner()
            }
        )
    }()
}

public extension DependencyValues {
    var musicPlayerClient: MusicPlayerClient {
        get { self[MusicPlayerClient.self] }
        set { self[MusicPlayerClient.self] = newValue }
    }
}
