import AVFoundation
import NitroModules

// Playback transport commands, routed through the C coordinator.
// Split out of HybridPlaybackEngine.swift; shared state and helpers
// remain on the main type.
extension HybridPlaybackEngine {
    // MARK: - Playback Commands (routed through C coordinator)

    func load(item: any HybridMediaItemSpec) throws -> Promise<Void> {
        #if DEBUG
            NSLog("[Aviation] load uri=%@", item.uri)
        #endif
        let promise = Promise<Void>()

        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }

            resolvePendingPromises() // superseded by this newer load

            // A newer transport change supersedes any route-caused pause.
            pausedByRouteChange = false
            transportCommandGeneration += 1

            // Update Swift-side item tracking
            _currentItem = item
            _currentItemSource = .direct

            loadPromise = promise

            // Build the C media item
            var coreItem = buildCoreItem(from: item)

            // Route through C coordinator — it will:
            // 1. Stop current playback if needed
            // 2. Set the current item
            // 3. Emit CURRENT_ITEM_CHANGED domain event
            // 4. Transition to Loading (emits DECODER_LOAD command)
            // 5. NOT track auto-play pending (key difference from loadAndPlay)
            aviation_coordinator_load(coord, &coreItem,
                                      AVIATION_SOURCE_DIRECT, -1)
        }

        return promise
    }

    func loadAndPlay(item: any HybridMediaItemSpec) throws -> Promise<Void> {
        #if DEBUG
            NSLog("[Aviation] loadAndPlay uri=%@", item.uri)
        #endif
        let promise = Promise<Void>()

        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }

            resolvePendingPromises() // superseded by this newer loadAndPlay

            // A newer transport change supersedes any route-caused pause.
            pausedByRouteChange = false
            transportCommandGeneration += 1

            // Update Swift-side item tracking
            _currentItem = item
            _currentItemSource = .direct

            loadPromise = promise

            // Build the C media item
            var coreItem = buildCoreItem(from: item)

            // Route through C coordinator — it will:
            // 1. Stop current playback if needed
            // 2. Set the current item
            // 3. Emit CURRENT_ITEM_CHANGED domain event
            // 4. Transition to Loading (emits DECODER_LOAD command)
            // 5. Track auto-play pending
            aviation_coordinator_load_and_play(coord, &coreItem,
                                               AVIATION_SOURCE_DIRECT, -1)
        }

        return promise
    }

    func play() throws -> Promise<Void> {
        let promise = Promise<Void>()

        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }

            // Already playing — no-op. Idle — need to load first.
            if _state == .playing {
                safeResolve(promise)
                return
            }
            if _state == .idle {
                safeReject(promise, error: NSError(domain: "aviation", code: -3,
                                                   userInfo: [NSLocalizedDescriptionKey: "Cannot play: no media loaded"]))
                return
            }
            // Buffering = auto-resumes when data arrives.
            if _state == .buffering {
                safeResolve(promise)
                return
            }

            safeResolve(playPromise) // superseded by this newer play()
            playPromise = promise

            // A newer transport change supersedes any route-caused pause.
            pausedByRouteChange = false
            transportCommandGeneration += 1

            // Route through C coordinator — it will emit DECODER_PLAY command
            aviation_coordinator_play(coord)

            // Fallback: if decoder is already playing, resolve immediately
            if avDecoder?.avPlayer?.timeControlStatus == .playing {
                _state = .playing
                playPromise = nil
                safeResolve(promise)
            }
        }

        return promise
    }

    func pause() throws -> Promise<Void> {
        let promise = Promise<Void>()
        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }
            // A newer transport change supersedes any route-caused pause.
            pausedByRouteChange = false
            transportCommandGeneration += 1

            aviation_coordinator_pause(coord)
            safeResolve(promise)
        }
        return promise
    }

    func stop() throws -> Promise<Void> {
        let promise = Promise<Void>()
        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }
            rejectPendingPromises(reason: "Player stopped")

            // A newer transport change supersedes any route-caused pause.
            pausedByRouteChange = false
            transportCommandGeneration += 1

            aviation_coordinator_stop(coord)
            _currentItem = nil
            _currentItemSource = .none
            fireCurrentItemChangeCallbacks()
            _mediaPosition = MediaPosition(
                currentMs: 0, durationMs: 0, seekableStartMs: 0, seekableEndMs: 0,
                isLive: false, progress: 0, liveEdgeOffsetMs: 0, isAtLiveEdge: false
            )
            safeResolve(promise)
        }
        return promise
    }

    func seekTo(positionMs: Double) throws -> Promise<Void> {
        let promise = Promise<Void>()
        DispatchQueue.main.async { [weak self] in
            guard let self, let coord = coordinator else { return }
            if _released {
                safeReject(promise, error: NSError(domain: "aviation", code: -10,
                                                   userInfo: [NSLocalizedDescriptionKey: "Engine released"]))
                return
            }
            safeResolve(seekPromise)
            seekPromise = promise

            aviation_coordinator_seek_to(coord, Int64(positionMs))

            // Resolve after a short delay (seek completion is detected via time observer).
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
                guard let self, seekPromise === promise else { return }
                seekPromise = nil
                safeResolve(promise)
            }
        }
        return promise
    }

    func release() throws {
        _released = true // Set synchronously BEFORE async dispatch
        commandGuardOwner = nil
        commandGuardBlock = nil
        playbackOutputOwner = nil
        playbackOutput = nil
        rejectPendingPromises(reason: "Engine released")
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            if let coord = coordinator {
                aviation_coordinator_destroy(coord)
                coordinator = nil
            }
            drmAdapter?.teardown()
            preloadAdapter?.teardown()
            cacheAdapter.teardown()
            decoder?.handleDestroy()
            nowPlayingAdapter.teardown()
            _currentItem = nil
            _state = .idle
            callbackLock.lock()
            listenerGeneration += 1
            clearAllCallbacks()
            callbackLock.unlock()
        }
    }
}
