import AVFoundation
import Foundation
import PlayerUtilities

// Tracks and stores the last thing that used AVPlayerClient
public struct SavedPlayerState {
    let url: String
    let time: CMTime
    let wasPlaying: Bool
    let owner: PlayerOwner
}

public actor AVPlayerClientState {
    private var periodicTimeObserver: Any?
    private var totalTimeObserver: Any?
    private var preloadedAssets: [(url: URL, asset: AVURLAsset)] = []
    private let maxPreloadedAssets = 3
    private var lastSeekTime: CMTime?
    private var preloadTasks: [Task<Void, Never>] = []
    private var statusObservations: [NSKeyValueObservation] = []
    private var timeUpdateInterval: TimeInterval = 0.5
    private var periodicTimeContinuation: AsyncStream<CMTime>.Continuation?
    private var currentOwner: PlayerOwner = .none
    private var savedPlayerState: SavedPlayerState?

    func setPeriodicTimeObserver(_ observer: Any?) {
        periodicTimeObserver = observer
    }

    func getPeriodicTimeObserver() -> Any? {
        return periodicTimeObserver
    }

    func setTotalTimeObserver(_ observer: Any?) {
        totalTimeObserver = observer
    }

    func getTotalTimeObserver() -> Any? {
        return totalTimeObserver
    }

    func clearPeriodicTimeObserver(from _: AVPlayer) {
        periodicTimeObserver = nil
    }

    func clearTotalTimeObserver() {
        totalTimeObserver = nil
    }

    func clearAllObservers(from player: AVPlayer) {
        clearPeriodicTimeObserver(from: player)
        clearTotalTimeObserver()
        clearStatusObservations(from: player)
    }

    func setPreloadedAsset(url: URL, asset: AVURLAsset) {
        if let index = preloadedAssets.firstIndex(where: { $0.url == url }) {
            preloadedAssets[index] = (url, asset)
        } else {
            preloadedAssets.append((url, asset))
            if preloadedAssets.count > maxPreloadedAssets {
                preloadedAssets.removeFirst()
            }
        }
    }

    func getPreloadedAsset(for url: URL? = nil) -> (url: URL, asset: AVURLAsset)? {
        if let url = url {
            return preloadedAssets.first { $0.url == url }
        } else if let firstAsset = preloadedAssets.first {
            return firstAsset
        }
        return nil
    }

    func removePreloadedAsset(for url: URL) {
        preloadedAssets.removeAll { $0.url == url }
    }

    func clearPreloadedAsset() {
        preloadedAssets.removeAll()
    }

    func setLastSeekTime(_ time: CMTime) {
        lastSeekTime = time
    }

    func getLastSeekTime() -> CMTime? {
        return lastSeekTime
    }

    func clearLastSeekTime() {
        lastSeekTime = nil
    }

    func addPreloadTask(_ task: Task<Void, Never>) {
        preloadTasks.append(task)
    }

    func cancelAllPreloadTasks() {
        for task in preloadTasks {
            task.cancel()
        }
        preloadTasks.removeAll()
    }

    func addStatusObservation(_ observation: NSKeyValueObservation) {
        statusObservations.append(observation)
    }

    func removeStatusObservation(_ observation: NSKeyValueObservation) {
        if let index = statusObservations.firstIndex(where: { $0 === observation }) {
            statusObservations.remove(at: index)
        }
    }

    func clearStatusObservations(from _: AVPlayer) {
        statusObservations.forEach { $0.invalidate() }
        statusObservations.removeAll()
    }

    func setTimeUpdateInterval(_ interval: TimeInterval) {
        timeUpdateInterval = interval
    }

    func getTimeUpdateInterval() -> TimeInterval {
        return timeUpdateInterval
    }

    func setPeriodicTimeContinuation(_ continuation: AsyncStream<CMTime>.Continuation?) {
        periodicTimeContinuation = continuation
    }

    func getPeriodicTimeContinuation() -> AsyncStream<CMTime>.Continuation? {
        return periodicTimeContinuation
    }

    func setOwner(_ owner: PlayerOwner) {
        currentOwner = owner
    }

    func getOwner() -> PlayerOwner {
        return currentOwner
    }

    func setSavedPlayerState(_ state: SavedPlayerState) {
        savedPlayerState = state
    }

    func getSavedPlayerState() -> SavedPlayerState? {
        return savedPlayerState
    }

    func clearSavedPlayerState() {
        savedPlayerState = nil
    }
}
