//
//  MPVVideoView.swift
//  Yattee
//
//  SwiftUI view that displays MPV video with custom controls overlay.
//

import SwiftUI

#if os(iOS) || os(macOS) || os(tvOS)

/// SwiftUI view that wraps MPV video rendering with custom controls.
struct MPVVideoView: View {
    @Environment(\.appEnvironment) private var appEnvironment
    let backend: MPVBackend
    @Bindable var playerState: PlayerState
    let playerService: PlayerService

    /// Whether to show playback controls
    var showsControls: Bool = true

    /// Whether to apply aspect ratio (enable when this view controls its own size)
    var appliesAspectRatio: Bool = false

    /// Whether this view is in widescreen layout mode
    var isWideScreenLayout: Bool = false

    /// Whether to show the debug overlay (disable when parent handles it)
    var showsDebugOverlay: Bool = true

    /// Callback for toggling panel visibility (widescreen layout)
    var onTogglePanel: (() -> Void)? = nil

    /// Whether panel is currently visible (widescreen layout)
    var isPanelVisible: Bool = true

    /// Which side the panel is on (widescreen layout)
    var panelSide: FloatingPanelSide = .right

    /// Callback for closing the video
    var onClose: (() -> Void)? = nil

    /// Callback for toggling fullscreen (widescreen videos only)
    var onToggleFullscreen: (() -> Void)? = nil

    /// Whether currently in fullscreen mode
    var isFullscreen: Bool = false

    /// Whether current video is widescreen (aspect ratio > 1.0)
    var isWidescreenVideo: Bool = false

    #if os(iOS)
    /// Callback for toggling orientation lock
    var onToggleOrientationLock: (() -> Void)? = nil

    /// Whether orientation is currently locked
    var isOrientationLocked: Bool = false

    /// Callback for toggling details visibility (fullscreen in portrait)
    var onToggleDetailsVisibility: (() -> Void)? = nil

    /// Callback for showing queue management sheet
    var onShowQueue: (() -> Void)? = nil
    #endif

    /// Debug stats for overlay (updated periodically when visible)
    @State private var debugStats: MPVDebugStats = MPVDebugStats()

    /// Timer for updating debug stats
    @State private var debugUpdateTimer: Timer?

    var body: some View {
        GeometryReader { geometry in
            let width = geometry.size.width
            let height = geometry.size.height

            ZStack {
                // MPV render surface - always show (fullscreen is now within the same view hierarchy)
                MPVRenderViewRepresentable(
                    backend: backend,
                    playerState: playerState
                )
                .frame(width: width, height: height)
                .background(Color.black)
                
                // Loading overlay - shown only if backend setup is not complete
                // (should be rare since backends are pre-warmed at app launch)
                if !backend.isSetupComplete {
                    Color.black
                        .overlay(
                            VStack(spacing: 16) {
                                ProgressView()
                                    .scaleEffect(1.5)
                                    .tint(.white)
                                Text("Initializing player...")
                                    .font(.caption)
                                    .foregroundColor(.secondary)
                            }
                        )
                        .transition(.opacity)
                        .zIndex(100)
                }

                // Custom controls overlay (MPV has no native controls)
                // Hide during PiP or when debug overlay is shown
                if showsControls && playerState.pipState != .active && !playerState.showDebugOverlay {
                    controlsView
                        .frame(width: width, height: height)
                }

                // Debug overlay - show when enabled and not in widescreen (widescreen has its own)
                if showsDebugOverlay && !isWideScreenLayout {
                    // Tap anywhere to dismiss
                    Color.clear
                        .contentShape(Rectangle())
                        .onTapGesture {
                            playerState.showDebugOverlay = false
                        }
                        .opacity(playerState.showDebugOverlay ? 1 : 0)
                        .allowsHitTesting(playerState.showDebugOverlay)

                    VStack {
                        HStack {
                            MPVDebugOverlay(
                                stats: debugStats,
                                isVisible: $playerState.showDebugOverlay,
                                isLandscape: false
                            )
                            Spacer()
                        }
                        Spacer()
                    }
                    .padding(12)
                    .allowsHitTesting(false)
                    .opacity(playerState.showDebugOverlay ? 1 : 0)
                    .animation(.easeInOut(duration: 0.2), value: playerState.showDebugOverlay)
                }
            }
            .frame(width: width, height: height)
        }
        .modifier(ConditionalAspectRatio(
            ratio: playerState.displayAspectRatio,
            applies: appliesAspectRatio
        ))
        .onAppear {
            #if os(iOS) || os(macOS)
            // Resume the MPV display link when the expanded video view appears.
            // MiniPlayerView pauses rendering when its video preview hides during expand;
            // PlayerService.playerSheetDidAppear skips resuming while PiP is active, so it
            // can't be relied on to resume. On macOS this also recovers the sheet re-open
            // black screen when the shared render view reappears without a window re-parent.
            backend.resumeRendering()
            #endif
            // Start debug updates if overlay is already visible when view appears
            if playerState.showDebugOverlay {
                startDebugUpdates()
            }
        }
        .onChange(of: playerState.showDebugOverlay) { _, isVisible in
            if isVisible {
                startDebugUpdates()
            } else {
                stopDebugUpdates()
            }
        }
        .onDisappear {
            stopDebugUpdates()
        }
    }

    private func startDebugUpdates() {
        // Stop any existing timer first
        stopDebugUpdates()

        // Update immediately
        debugStats = backend.getDebugStats()

        // Then update every second
        debugUpdateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            Task { @MainActor in
                self.debugStats = self.backend.getDebugStats()
            }
        }
    }

    private func stopDebugUpdates() {
        debugUpdateTimer?.invalidate()
        debugUpdateTimer = nil
    }

    // MARK: - Controls View

    @ViewBuilder
    private var controlsView: some View {
        #if os(iOS)
        PlayerControlsView(
            playerState: playerState,
            onPlayPause: {
                playerService.togglePlayPause()
            },
            onSeek: { time in
                await playerService.seek(to: time)
            },
            onSeekForward: { seconds in
                playerService.seekForward(by: seconds)
            },
            onSeekBackward: { seconds in
                playerService.seekBackward(by: seconds)
            },
            onToggleFullscreen: onToggleFullscreen,
            isFullscreen: isFullscreen,
            isWidescreenVideo: isWidescreenVideo,
            onClose: onClose,
            onTogglePiP: {
                backend.togglePiP()
            },
            onToggleDebug: {
                withAnimation(.easeInOut(duration: 0.2)) {
                    playerState.showDebugOverlay.toggle()
                }
            },
            isWideScreenLayout: isWideScreenLayout,
            onTogglePanel: onTogglePanel,
            isPanelVisible: isPanelVisible,
            panelSide: panelSide,
            onToggleOrientationLock: onToggleOrientationLock,
            isOrientationLocked: isOrientationLocked,
            onToggleDetailsVisibility: onToggleDetailsVisibility,
            onPlayNext: {
                await playerService.playNext()
            },
            onPlayPrevious: {
                await playerService.playPrevious()
            },
            onShowQueue: onShowQueue,
            onVolumeChanged: { [weak appEnvironment] volume in
                playerService.currentBackend?.volume = volume
                appEnvironment?.settingsManager.playerVolume = volume
                appEnvironment?.remoteControlCoordinator.broadcastStateUpdate()
            },
            onMuteToggled: { [weak appEnvironment] in
                let newMuted = !playerState.isMuted
                playerService.currentBackend?.isMuted = newMuted
                playerState.isMuted = newMuted
                appEnvironment?.remoteControlCoordinator.broadcastStateUpdate()
            },
            currentVideo: playerState.currentVideo,
            availableCaptions: playerService.availableCaptions,
            currentCaption: playerService.currentCaption,
            availableStreams: playerService.availableStreams,
            currentStream: playerState.currentStream,
            currentAudioStream: playerState.currentAudioStream,
            onRateChanged: { rate in
                playerState.rate = rate
                playerService.currentBackend?.rate = Float(rate.rawValue)
            },
            onCaptionSelected: { caption in
                playerService.loadCaption(caption)
            },
            onStreamSelected: { stream, audioStream in
                Task {
                    await playerService.selectStreamManually(stream, audioStream: audioStream)
                }
            },
            embeddedAudioTracks: playerService.embeddedAudioTracks,
            embeddedSubtitleTracks: playerService.embeddedSubtitleTracks,
            currentEmbeddedAudioTrackID: playerService.selectedEmbeddedAudioTrackID,
            currentEmbeddedSubtitleTrackID: playerService.selectedEmbeddedSubtitleTrackID,
            embeddedVideoTrack: playerService.primaryEmbeddedVideoTrack,
            onEmbeddedAudioTrackSelected: { trackID in
                playerService.selectEmbeddedAudioTrack(trackID)
            },
            onEmbeddedSubtitleTrackSelected: { trackID in
                playerService.selectEmbeddedSubtitleTrack(trackID)
            }
        )
        #elseif os(macOS)
        MacOSPlayerControlsView(
            playerState: playerState,
            onPlayPause: {
                playerService.togglePlayPause()
            },
            onSeek: { time in
                await playerService.seek(to: time)
            },
            onSeekForward: { seconds in
                playerService.seekForward(by: seconds)
            },
            onSeekBackward: { seconds in
                playerService.seekBackward(by: seconds)
            },
            onToggleFullscreen: onToggleFullscreen,
            isFullscreen: isFullscreen,
            onClose: onClose,
            onPlayNext: {
                await playerService.playNext()
            },
            onVolumeChanged: { [weak appEnvironment] volume in
                playerService.currentBackend?.volume = volume
                appEnvironment?.settingsManager.playerVolume = volume
                appEnvironment?.remoteControlCoordinator.broadcastStateUpdate()
            },
            onMuteToggled: { [weak appEnvironment] in
                let newMuted = !playerState.isMuted
                playerService.currentBackend?.isMuted = newMuted
                playerState.isMuted = newMuted
                appEnvironment?.remoteControlCoordinator.broadcastStateUpdate()
            },
            onRateChanged: { rate in
                playerState.rate = rate
                playerService.currentBackend?.rate = Float(rate.rawValue)
            }
        )
        #else
        // tvOS uses its own player controls (TVPlayerControlsView)
        EmptyView()
        #endif
    }
}

/// Conditionally applies aspect ratio modifier
struct ConditionalAspectRatio: ViewModifier {
    let ratio: Double
    let applies: Bool

    func body(content: Content) -> some View {
        if applies {
            content
                .aspectRatio(ratio, contentMode: .fit)
        } else {
            content
        }
    }
}

// MARK: - Preview

#Preview {
    // Preview requires mock objects
    ZStack {
        Color.black
        Text("MPV Video View")
            .foregroundStyle(.white)
    }
    .aspectRatio(16/9, contentMode: .fit)
}

#endif
