//
//  WideScreenPlayerLayout.swift
//  Yattee
//
//  Widescreen layout with video player and floating details panel.
//  Supports two modes:
//  - Overlay: Panel floats over full-width player, appears with controls
//  - Pinned: Player resizes to make room for panel
//

import SwiftUI

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

struct WideScreenPlayerLayout<PlayerContent: View>: View {
    @Environment(\.appEnvironment) private var appEnvironment

    let playerControlsLayout: PlayerControlsLayout

    @ViewBuilder let playerContent: (
        _ onTogglePanel: @escaping () -> Void,
        _ isPanelVisible: Bool,
        _ isPanelPinned: Bool,
        _ panelSide: FloatingPanelSide,
        _ onHidePanel: @escaping () -> Void,
        _ leadingSafeArea: CGFloat,
        _ trailingSafeArea: CGFloat,
        _ fullWidth: CGFloat,
        _ fullHeight: CGFloat
    ) -> PlayerContent

    // Callbacks for panel actions
    let onChannelTap: (() -> Void)?
    let onFullscreen: (() -> Void)?
    @State private var controlsVisible = false
    @State private var isPanelVisible = false // Local state synced with settingsManager
    @State private var lastVideoId: String? // Track video ID to detect actual video changes
    @State private var panelWidth: CGFloat = FloatingDetailsPanel.defaultPanelWidth

    private var settingsManager: SettingsManager? { appEnvironment?.settingsManager }
    private var playerState: PlayerState? { appEnvironment?.playerService.state }

    // Read video from playerState for reactive updates
    private var video: Video? { playerState?.currentVideo }

    private var panelSide: FloatingPanelSide {
        settingsManager?.floatingDetailsPanelSide ?? .left
    }

    private var isPanelPinned: Bool {
        settingsManager?.landscapeDetailsPanelPinned ?? false
    }

    /// Base panel width including grabber and padding (without safe area)
    private var basePanelWidth: CGFloat {
        panelWidth + 20 + 12 // panelWidth + grabber (20pt) + outer edge padding (12pt)
    }

    /// Maximum panel width based on available width and pinned state
    private func maxPanelWidth(availableWidth: CGFloat) -> CGFloat {
        let minWidth = FloatingDetailsPanel.minPanelWidth
        let minVideoWidth: CGFloat = 400
        if isPanelPinned {
            // Pinned: leave at least 300pt for video
            return max(minWidth, availableWidth - minVideoWidth)
        } else {
            // Unpinned: 80% of available width, capped at 1000pt
            return max(minWidth, min(availableWidth * 0.8, 1000))
        }
    }

    /// Calculate safe area padding for panel outer edge
    /// Always use full safe area - rounded corner side is already small
    private func panelSafeAreaPadding(safeAreaLeft: CGFloat, safeAreaRight: CGFloat) -> CGFloat {
        if panelSide == .left {
            return safeAreaLeft
        } else {
            return safeAreaRight
        }
    }

    /// Total panel width including safe area padding
    /// Must include the same padding applied to the panel view
    private func totalPanelWidth(safeAreaLeft: CGFloat, safeAreaRight: CGFloat) -> CGFloat {
        basePanelWidth + panelSafeAreaPadding(safeAreaLeft: safeAreaLeft, safeAreaRight: safeAreaRight)
    }

    /// Whether to show the panel
    private var shouldShowPanel: Bool {
        isPanelVisible
    }

    /// Toggle panel visibility from controls
    private func togglePanelFromControls() {
        withAnimation(.easeInOut(duration: 0.3)) {
            isPanelVisible.toggle()
            settingsManager?.landscapeDetailsPanelVisible = isPanelVisible
        }
    }

    /// Hide the panel completely
    private func hidePanel() {
        isPanelVisible = false
        settingsManager?.landscapeDetailsPanelVisible = false
    }

    /// Get safe area insets from window scene (geometry reader insets are 0 when .ignoresSafeArea is used)
    private var windowSafeAreaInsets: EdgeInsets {
        #if os(iOS)
        guard let windowScene = UIApplication.shared.connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first(where: { $0.activationState == .foregroundActive }),
              let window = windowScene.windows.first
        else { return EdgeInsets() }
        let insets = window.safeAreaInsets
        return EdgeInsets(top: insets.top, leading: insets.left, bottom: insets.bottom, trailing: insets.right)
        #else
        return EdgeInsets()
        #endif
    }

    /// Get full screen bounds from window scene (ignores safe area constraints from parent)
    /// On macOS, this is not used - geometry reader size is used directly
    private var windowSceneBounds: CGSize {
        #if os(iOS)
        guard let windowScene = UIApplication.shared.connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first(where: { $0.activationState == .foregroundActive }),
              let window = windowScene.windows.first
        else {
            // Fallback to screen bounds
            let screen = UIScreen.main.bounds
            return CGSize(width: max(screen.width, screen.height), height: min(screen.width, screen.height))
        }
        // Window frame gives actual size including orientation
        return window.frame.size
        #else
        return .zero // Not used on macOS - geometry reader size is used directly
        #endif
    }

    var body: some View {
        GeometryReader { geometry in
            // On iOS: Use window bounds to get full size (avoids safe area constraint from parent)
            // On macOS: Use geometry reader size directly (no safe area concerns, window is the source of truth)
            #if os(iOS)
            let windowBounds = windowSceneBounds
            let availableWidth = windowBounds.width
            let availableHeight = windowBounds.height
            let safeAreaLeft = windowSafeAreaInsets.leading
            let safeAreaRight = windowSafeAreaInsets.trailing
            #else
            let availableWidth = geometry.size.width
            let availableHeight = geometry.size.height
            let safeAreaLeft: CGFloat = 0
            let safeAreaRight: CGFloat = 0
            #endif

            // Safe area strategy:
            // - Left side (Dynamic Island): Always respect full safe area - content would be cut off
            // - Right side (rounded corners): Can extend to edge - content still visible
            // - Panel: Use half safe area on outer edge to get closer to edge while keeping content visible

            // Calculate total panel width including safe area padding
            let totalPanelW = totalPanelWidth(safeAreaLeft: safeAreaLeft, safeAreaRight: safeAreaRight)

            // Detect which side has Dynamic Island based on interface orientation
            // Note: Can't use safe area comparison because both sides have similar values (~62px each)
            #if os(iOS)
            let interfaceOrientation = UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .first?
                .interfaceOrientation ?? .unknown
            // landscapeLeft = home/gesture bar on left = Dynamic Island on RIGHT
            // landscapeRight = home/gesture bar on right = Dynamic Island on LEFT
            let isDynamicIslandOnLeft = interfaceOrientation == .landscapeRight
            let isDynamicIslandOnRight = interfaceOrientation == .landscapeLeft
            #else
            let isDynamicIslandOnLeft = safeAreaLeft > safeAreaRight
            let isDynamicIslandOnRight = safeAreaRight > safeAreaLeft
            #endif

            // Calculate Dynamic Island safe area on the side opposite to panel
            // Video must not extend into Dynamic Island
            let opposingDynamicIslandSafeArea: CGFloat = {
                guard isPanelPinned && isPanelVisible else { return 0 }
                if panelSide == .right && isDynamicIslandOnLeft {
                    return safeAreaLeft
                } else if panelSide == .left && isDynamicIslandOnRight {
                    return safeAreaRight
                }
                return 0
            }()

            // Calculate player width based on pinned state
            let _ = isPanelPinned && isPanelVisible
                ? availableWidth - totalPanelW - opposingDynamicIslandSafeArea
                : availableWidth

            ZStack {
                // Black background - extends under status bar and home indicator
                Color.black
                    .ignoresSafeArea(.all)

                // Calculate safe areas to pass to player content
                // When panel is pinned, the side with the panel needs space for it
                // The opposite side needs space for Dynamic Island if present
                let leadingSafeArea: CGFloat = {
                    guard isPanelPinned && isPanelVisible else { return 0 }
                    if panelSide == .left {
                        return totalPanelW
                    } else if isDynamicIslandOnLeft {
                        return safeAreaLeft
                    }
                    return 0
                }()

                let trailingSafeArea: CGFloat = {
                    guard isPanelPinned && isPanelVisible else { return 0 }
                    if panelSide == .right {
                        return totalPanelW
                    } else if isDynamicIslandOnRight {
                        return safeAreaRight
                    }
                    return 0
                }()

                // Player content - fills entire space, handles safe areas internally
                // Pass full geometry so playerContent doesn't need its own GeometryReader
                playerContent(
                    togglePanelFromControls,
                    isPanelVisible,
                    isPanelPinned,
                    panelSide,
                    { withAnimation(.easeInOut(duration: 0.3)) { hidePanel() } },
                    leadingSafeArea,
                    trailingSafeArea,
                    availableWidth,
                    availableHeight
                )

                // Panel container
                // Dynamic Island side needs full safe area, rounded corner side needs half
                let panelOuterPadding = panelSafeAreaPadding(safeAreaLeft: safeAreaLeft, safeAreaRight: safeAreaRight)

                // Panel - always in hierarchy, visibility controlled via opacity
                panelContainer(
                    safeAreaPadding: panelOuterPadding,
                    availableWidth: availableWidth,
                    availableHeight: availableHeight
                )
                .opacity(shouldShowPanel ? 1 : 0)
                .allowsHitTesting(shouldShowPanel)

            }
            .animation(.easeInOut(duration: 0.3), value: isPanelPinned)
            .animation(.easeInOut(duration: 0.3), value: isPanelVisible)
            .animation(.easeInOut(duration: 0.3), value: panelSide)
            .animation(.easeInOut(duration: 0.3), value: controlsVisible)
        }
        .onChange(of: playerState?.controlsVisible) { _, newValue in
            withAnimation(.easeInOut(duration: 0.2)) {
                controlsVisible = newValue ?? false
            }
        }
        .onChange(of: video?.id) { _, newId in
            // Only reset panel state when video actually changes (not on view recreation)
            guard let newId, lastVideoId != newId.videoID else { return }
            lastVideoId = newId.videoID

            // Reset comments state (stored in PlayerState)
            playerState?.comments = []
            playerState?.commentsState = .idle
            playerState?.commentsContinuation = nil

            // Panel state remains unchanged - don't modify visibility or expanded state
        }
        .onAppear {
            controlsVisible = playerState?.controlsVisible ?? false

            // Track initial video ID
            if lastVideoId == nil {
                lastVideoId = video?.id.videoID
            }

            // Initialize panel state from settings (respect user/system-set visibility)
            isPanelVisible = settingsManager?.landscapeDetailsPanelVisible ?? false
            // Don't override settingsManager values - they were already set by ExpandedPlayerSheet

            // Load saved panel width from settings
            if let savedWidth = settingsManager?.floatingDetailsPanelWidth, savedWidth > 0 {
                panelWidth = savedWidth
            }
        }
        .onChange(of: panelWidth) { _, newWidth in
            // Persist panel width changes to settings
            settingsManager?.floatingDetailsPanelWidth = newWidth
        }
        .ignoresSafeArea(.all)
        #if os(iOS)
        .persistentSystemOverlays(.hidden)
        .playerStatusBarHidden(true)
        #endif
    }

    // MARK: - Panel Container

    /// Panel container for the floating details panel
    @ViewBuilder
    private func panelContainer(safeAreaPadding: CGFloat, availableWidth: CGFloat, availableHeight: CGFloat) -> some View {
        // Panel dimensions
        let panelHeight = availableHeight - 24 // 12pt top + 12pt bottom padding

        ZStack(alignment: panelSide == .right ? .topTrailing : .topLeading) {
            detailsPanel(availableWidth: availableWidth)
                .frame(height: panelHeight)
                .padding(.top, 12)
                .padding(.bottom, 12)
                .padding(panelSide == .left ? .leading : .trailing, safeAreaPadding + 12)
                .transition(.scale(scale: 0.9, anchor: panelSide == .right ? .topTrailing : .topLeading).combined(with: .opacity))
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: panelSide == .right ? .topTrailing : .topLeading)
    }

    // MARK: - Details Panel

    @ViewBuilder
    private func detailsPanel(availableWidth: CGFloat) -> some View {
        FloatingDetailsPanel(
            onPinToggle: {
                if isPanelPinned {
                    // Unpinning - convert to floating mode (don't close)
                    withAnimation(.easeInOut(duration: 0.3)) {
                        settingsManager?.landscapeDetailsPanelPinned = false
                    }
                } else {
                    // Pinning - ensure panel is visible first
                    isPanelVisible = true
                    settingsManager?.landscapeDetailsPanelVisible = true

                    // Animate panscan to zero and pin concurrently for smooth transition
                    appEnvironment?.navigationCoordinator.animatePanscanToZero(completion: nil)
                    withAnimation(.easeInOut(duration: 0.3)) {
                        settingsManager?.landscapeDetailsPanelPinned = true
                    }
                }
            },
            onAlignmentToggle: {
                withAnimation(.easeInOut(duration: 0.3)) {
                    settingsManager?.floatingDetailsPanelSide = panelSide.opposite
                }
            },
            isPinned: isPanelPinned,
            panelSide: panelSide,
            onChannelTap: onChannelTap,
            onFullscreen: onFullscreen,
            panelWidth: $panelWidth,
            availableWidth: availableWidth,
            maxPanelWidth: maxPanelWidth(availableWidth: availableWidth),
            playerControlsLayout: playerControlsLayout
        )
    }

    /// Returns the first enabled Yattee Server instance URL, if any.
    private var yatteeServerURL: URL? {
        appEnvironment?.instancesManager.yatteeServerInstances.first { $0.isEnabled }?.url
    }
}

#endif
