//
//  PortraitDetailsPanel.swift
//  Yattee
//
//  Details panel for portrait mode with no header bar.
//  Shows video info, description, comments pill, and queue pill.
//

import SwiftUI

#if os(iOS)

struct PortraitDetailsPanel: View {
    @Environment(\.appEnvironment) private var appEnvironment

    let onChannelTap: (() -> Void)?
    let playerControlsLayout: PlayerControlsLayout
    let onFullscreen: (() -> Void)?
    let pillsOverlayOpacity: CGFloat

    // Drag gesture callbacks
    var onDragChanged: ((CGFloat) -> Void)?
    var onDragEnded: ((CGFloat, CGFloat) -> Void)?
    var onDragCancelled: (() -> Void)?
    @GestureState private var isDraggingHandle: Bool = false
    @State private var isCommentsExpanded: Bool = false
    @State private var showFormattedDate = false
    @State private var scrollOffset: CGFloat = 0
    @State private var scrollToTopTrigger: Bool = false
    @State private var showQueueSheet: Bool = false
    @State private var showPlaylistSheet: Bool = false

    private var settingsManager: SettingsManager? { appEnvironment?.settingsManager }
    private var accentColor: Color { settingsManager?.resolvedAccentColor ?? .accentColor }
    private var playerService: PlayerService? { appEnvironment?.playerService }
    private var playerState: PlayerState? { playerService?.state }
    private var isPanelDragging: Bool { appEnvironment?.navigationCoordinator.isPanelDragging ?? false }

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

    // Comments helpers
    private var comments: [Comment] { playerState?.comments ?? [] }
    private var commentsState: CommentsLoadState { playerState?.commentsState ?? .idle }

    // Video details helpers
    private var videoDetailsState: VideoDetailsLoadState { playerState?.videoDetailsState ?? .idle }

    // Queue helpers
    private var queue: [QueuedVideo] { playerState?.queue ?? [] }
    private var history: [QueuedVideo] { playerState?.history ?? [] }
    private var isQueueEnabled: Bool { settingsManager?.queueEnabled ?? true }

    // Player pill helpers
    private var playerPillSettings: PlayerPillSettings {
        playerControlsLayout.effectivePlayerPillSettings
    }
    private var shouldShowPlayerPill: Bool {
        playerPillSettings.visibility.isVisible(isWideLayout: false) &&
        !playerPillSettings.buttons.isEmpty
    }

    // Comments pill helpers
    private var commentsPillMode: CommentsPillMode {
        playerPillSettings.effectiveCommentsPillMode
    }
    private var shouldShowCommentsPill: Bool {
        playerPillSettings.shouldShowCommentsPill
    }
    private var isCommentsPillAlwaysCollapsed: Bool {
        playerPillSettings.isCommentsPillAlwaysCollapsed
    }

    private var hasVideoDescription: Bool {
        !(video?.description ?? "").isEmpty
    }

    /// Whether any pills are currently visible in the overlay
    private var hasPillsVisible: Bool {
        let hasCommentsPill = commentsState == .loaded && !comments.isEmpty && shouldShowCommentsPill
        return shouldShowPlayerPill || hasCommentsPill
    }

    /// Whether comments pill is shown expanded on its own row (above player pill)
    private var hasExpandedCommentsPill: Bool {
        let hasCommentsPill = commentsState == .loaded && !comments.isEmpty && shouldShowCommentsPill
        return hasCommentsPill && shouldShowPlayerPill && !isCommentsPillAlwaysCollapsed && hasVideoDescription
    }

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

    /// Returns the DeArrow title if available and enabled, otherwise the original title.
    private func displayTitle(for video: Video) -> String {
        appEnvironment?.deArrowBrandingProvider.title(for: video) ?? video.title
    }

    // MARK: - Drag Handle

    private var dragHandle: some View {
        Capsule()
            .fill(Color.secondary.opacity(0.4))
            .frame(width: 36, height: 4)
            .padding(.top, 5)
            .padding(.bottom, 2)
    }

    // MARK: - Bottom Fade Overlay

    @ViewBuilder
    private var bottomFadeOverlay: some View {
        // Fade height: tall when comments expanded on own row, medium for single-row pills, short when no pills
        // When expanded: 24pt padding + 52pt player pill + 12pt spacing + ~26pt (half comments pill) ≈ 115pt
        let fadeHeight: CGFloat = hasExpandedCommentsPill ? 115 : (hasPillsVisible ? 70 : 25)

        ZStack {
            // Touch-blocking layer (invisible but intercepts touches)
            Color.white.opacity(0.001)
                .frame(height: fadeHeight)
                .contentShape(Rectangle())

            // Visual gradient
            LinearGradient(
                colors: [.clear, Color(.systemBackground)],
                startPoint: .top,
                endPoint: .bottom
            )
            .frame(height: fadeHeight)
            .allowsHitTesting(false)
        }
        .frame(height: fadeHeight)
        .animation(.easeInOut(duration: 0.2), value: hasExpandedCommentsPill)
        .animation(.easeInOut(duration: 0.2), value: hasPillsVisible)
    }

    var body: some View {
        if let video {
            ZStack(alignment: .top) {
                // Scrollable content (extends to top, scrolls under drag handle)
                ScrollViewReader { proxy in
                    ScrollView {
                        VStack(alignment: .leading, spacing: 0) {
                            // Anchor for scroll-to-top
                            Color.clear
                                .frame(height: 0)
                                .id("panelTop")

                            // Top padding so content isn't initially obscured by drag handle
                            Color.clear
                                .frame(height: 10)

                            videoInfo(video)

                            Divider()
                                .padding(.horizontal)

                            infoTabSection(video)
                        }
                    }
                    .background(Color(.systemBackground))
                    .background {
                        // UIKit gesture handler for smooth overscroll-to-collapse
                        OverscrollGestureView(
                            isEnabled: !isDraggingHandle,
                            onDragChanged: { offset in
                                onDragChanged?(offset)
                            },
                            onDragEnded: { offset, predicted in
                                onDragEnded?(offset, predicted)
                            }
                        )
                    }
                    .scrollDisabled(isDraggingHandle)
                    .onScrollGeometryChange(for: CGFloat.self) { geometry in
                        geometry.contentOffset.y
                    } action: { _, newValue in
                        // Update scrollOffset for pills visibility
                        scrollOffset = max(0, newValue)
                    }
                    .onChange(of: scrollToTopTrigger) { _, _ in
                        withAnimation(.easeInOut(duration: 0.3)) {
                            proxy.scrollTo("panelTop", anchor: .top)
                        }
                    }
                }
                .overlay(alignment: .bottom) {
                    bottomFadeOverlay
                }

                // Extended drag hit area overlay - visual handle at top, hit area extends down
                VStack(spacing: 0) {
                    dragHandle
                    Spacer()
                }
                .frame(height: 50)
                .frame(maxWidth: .infinity)
                .background(alignment: .top) {
                    LinearGradient(
                        colors: [Color(.systemBackground), Color(.systemBackground).opacity(0)],
                        startPoint: .top,
                        endPoint: .bottom
                    )
                    .frame(height: 25)
                }
                .contentShape(Rectangle())
                .highPriorityGesture(
                    DragGesture(coordinateSpace: .global)
                        .updating($isDraggingHandle) { _, state, _ in
                            state = true
                        }
                        .onChanged { value in
                            // Allow both upward (negative) and downward (positive) drag
                            let offset = value.translation.height
                            onDragChanged?(offset)
                        }
                        .onEnded { value in
                            onDragEnded?(value.translation.height, value.predictedEndTranslation.height)
                        }
                )
                .onChange(of: isDraggingHandle) { oldValue, newValue in
                    // When gesture state resets (true -> false) but onEnded wasn't called,
                    // the gesture was cancelled (app backgrounded, control center, etc.)
                    if oldValue && !newValue {
                        // Small delay to let onEnded fire first if it will
                        DispatchQueue.main.async {
                            // If still dragging according to parent, it was a cancellation
                            if appEnvironment?.navigationCoordinator.isPanelDragging == true {
                                onDragCancelled?()
                            }
                        }
                    }
                }
            }
            .background(Color(.systemBackground))
            .clipShape(RoundedRectangle(cornerRadius: 16))
            // Pills overlay at bottom
            .overlay {
                if !isCommentsExpanded {
                    pillsOverlay
                        .opacity(pillsOverlayOpacity)
                        .allowsHitTesting(pillsOverlayOpacity > 0.01)
                }
            }
            // Expanded comments overlay
            .overlay {
                let expanded = isCommentsExpanded
                ExpandedCommentsView(
                    videoID: video.id.videoID,
                    onClose: collapseComments,
                    onDismissGestureEnded: handleCommentsDismissGestureEnded,
                    onDragChanged: onDragChanged,
                    onDragEnded: onDragEnded
                )
                .clipShape(RoundedRectangle(cornerRadius: 16))
                .visualEffect { content, proxy in
                    content.offset(y: expanded ? 0 : proxy.size.height)
                }
                .opacity(expanded ? 1 : 0)
                .allowsHitTesting(expanded)
                .background(
                    GeometryReader { commentsGeometry in
                        Color.clear
                            .onChange(of: commentsGeometry.frame(in: .global)) { _, newFrame in
                                if isCommentsExpanded {
                                    appEnvironment?.navigationCoordinator.commentsFrame = newFrame
                                }
                            }
                            .onChange(of: isCommentsExpanded) { _, expanded in
                                if expanded {
                                    appEnvironment?.navigationCoordinator.commentsFrame = commentsGeometry.frame(in: .global)
                                } else {
                                    appEnvironment?.navigationCoordinator.commentsFrame = .zero
                                }
                            }
                    }
                )
            }
            .animation(.smooth(duration: 0.3), value: isCommentsExpanded)
            .onChange(of: video.id) { _, _ in
                isCommentsExpanded = false
            }
            .onChange(of: isCommentsExpanded) { _, newValue in
                appEnvironment?.navigationCoordinator.isCommentsExpanded = newValue
            }
            .sheet(isPresented: $showQueueSheet) {
                QueueManagementSheet()
            }
            .sheet(isPresented: $showPlaylistSheet) {
                if let currentVideo = playerState?.currentVideo {
                    PlaylistSelectorSheet(video: currentVideo)
                }
            }
        }
    }

    // MARK: - Pills Overlay

    @ViewBuilder
    private var pillsOverlay: some View {
        GeometryReader { geometry in
            let isScrolled = scrollOffset > 20
            let hasCommentsPill = commentsState == .loaded && !comments.isEmpty && shouldShowCommentsPill
            let isPlaying = playerState?.playbackState == .playing
            let hasNext = playerState?.hasNext ?? false
            // On narrow devices, use smaller side buttons so the player pill gets more horizontal space
            let isCompactPillRow = geometry.size.width <= 390
            let collapsedCommentWidth: CGFloat = isCompactPillRow ? 40 : 52
            let scrollButtonWidth: CGFloat = isCompactPillRow ? 40 : 52
            let pillRowSpacing: CGFloat = isCompactPillRow ? 8 : 12

            VStack {
                Spacer()

                VStack(spacing: 12) {
                    // Comments pill - on its own row (only when player pill exists AND not collapsed)
                    let isPillCollapsed = isScrolled || isCommentsPillAlwaysCollapsed || !hasVideoDescription
                    if hasCommentsPill, let firstComment = comments.first, shouldShowPlayerPill, !isPillCollapsed {
                        CommentsPillView(
                            comment: firstComment,
                            isCollapsed: false,
                            onTap: expandComments
                        )
                    }

                    // Bottom row
                    bottomPillsRow(
                        hasCommentsPill: hasCommentsPill,
                        collapsedCommentWidth: collapsedCommentWidth,
                        pillRowSpacing: pillRowSpacing,
                        isCompact: isCompactPillRow,
                        isPlaying: isPlaying,
                        hasNext: hasNext,
                        scrollButtonWidth: scrollButtonWidth,
                        availableWidth: geometry.size.width,
                        isScrolled: isScrolled
                    )
                }
                .frame(maxWidth: .infinity)
                .padding(.horizontal, 16)
                .padding(.bottom, 24)
            }
            // Only animate when comments load or player pill changes, not on panel size changes
            // Suppress animations during panel drag to prevent independent animation on release
            .animation(isPanelDragging ? nil : .spring(response: 0.35, dampingFraction: 0.8), value: hasCommentsPill)
            .animation(isPanelDragging ? nil : .spring(response: 0.3, dampingFraction: 0.7), value: shouldShowPlayerPill)
            .animation(isPanelDragging ? nil : .easeInOut(duration: 0.2), value: isScrolled)
        }
    }

    /// Bottom row of pills (comments, player pill, scroll button) - extracted to help compiler
    @ViewBuilder
    private func bottomPillsRow(
        hasCommentsPill: Bool,
        collapsedCommentWidth: CGFloat,
        pillRowSpacing: CGFloat,
        isCompact: Bool,
        isPlaying: Bool,
        hasNext: Bool,
        scrollButtonWidth: CGFloat,
        availableWidth: CGFloat,
        isScrolled: Bool
    ) -> some View {
        // Calculate max pill width: available width minus placeholders and spacing
        // Layout: [16 padding] [left] [spacing] [PILL] [spacing] [right] [16 padding]
        let edgeElementsWidth: CGFloat = 32 + collapsedCommentWidth + scrollButtonWidth + (pillRowSpacing * 2)
        let maxPillWidth = max(availableWidth - edgeElementsWidth, 150)

        HStack(spacing: pillRowSpacing) {
            // Left side: comments pill or placeholder
            let isPillCollapsed = isScrolled || isCommentsPillAlwaysCollapsed || !hasVideoDescription
            if hasCommentsPill, let firstComment = comments.first {
                if !shouldShowPlayerPill {
                    // No player pill: show comments in bottom row (respect collapse mode)
                    CommentsPillView(
                        comment: firstComment,
                        isCollapsed: isPillCollapsed,
                        fillWidth: !isPillCollapsed,
                        compact: isCompact && isPillCollapsed,
                        onTap: expandComments
                    )
                    .frame(maxWidth: isPillCollapsed ? nil : 400)
                    .frame(maxWidth: .infinity, alignment: .leading)
                } else if isPillCollapsed {
                    // Player pill exists AND collapsed: show comments button on left
                    CommentsPillView(
                        comment: firstComment,
                        isCollapsed: true,
                        compact: isCompact,
                        onTap: expandComments
                    )
                } else {
                    // Placeholder (expanded comments on its own row)
                    Color.clear.frame(width: collapsedCommentWidth, height: collapsedCommentWidth)
                }
            } else if shouldShowPlayerPill {
                Color.clear.frame(width: collapsedCommentWidth, height: collapsedCommentWidth)
            }

            // Player pill
            if shouldShowPlayerPill {
                PlayerPillView(
                    settings: playerPillSettings,
                    maxWidth: maxPillWidth,
                    isWideLayout: false,
                    isPlaying: isPlaying,
                    hasNext: hasNext,
                    queueCount: queue.count,
                    queueModeIcon: playerState?.queueMode.icon ?? "list.bullet",
                    isPlayPauseDisabled: playerState?.playbackState == .loading,
                    isOrientationLocked: appEnvironment?.settingsManager.inAppOrientationLock ?? false,
                    video: video,
                    playbackRate: playerState?.rate ?? .x1,
                    showingPlaylistSheet: $showPlaylistSheet,
                    onQueueTap: { showQueueSheet = true },
                    onPrevious: { Task { await playerService?.playPrevious() } },
                    onPlayPause: { playerService?.togglePlayPause() },
                    onNext: { Task { await playerService?.playNext() } },
                    onSeek: { signedSeconds in
                        if signedSeconds >= 0 {
                            playerService?.seekForward(by: signedSeconds)
                        } else {
                            playerService?.seekBackward(by: -signedSeconds)
                        }
                    },
                    onClose: {
                        appEnvironment?.queueManager.clearQueue()
                        playerService?.stop()
                        appEnvironment?.navigationCoordinator.isPlayerExpanded = false
                    },
                    onAirPlay: { /* AirPlay - handled by system */ },
                    onPiP: {
                        #if os(iOS)
                        if let mpvBackend = playerService?.currentBackend as? MPVBackend {
                            mpvBackend.togglePiP()
                        }
                        #endif
                    },
                    onOrientationLock: {
                        #if os(iOS)
                        appEnvironment?.settingsManager.inAppOrientationLock.toggle()
                        #endif
                    },
                    onFullscreen: { onFullscreen?() },
                    onRateChanged: { rate in
                        playerState?.rate = rate
                        playerService?.currentBackend?.rate = Float(rate.rawValue)
                    }
                )
            }

            // Spacer to push scroll button trailing when no player pill
            if !shouldShowPlayerPill {
                Spacer()
            }

            // Scroll button or placeholder
            if scrollOffset > 20 {
                Button {
                    scrollToTopTrigger.toggle()
                } label: {
                    Image(systemName: "arrow.up")
                        .font(.system(size: isCompact ? 16 : 18, weight: .semibold))
                        .foregroundStyle(.primary)
                        .frame(width: isCompact ? 28 : 32, height: isCompact ? 28 : 32)
                        .padding(isCompact ? 6 : 10)
                        .contentShape(Circle())
                }
                .buttonStyle(.plain)
                .glassBackground(.regular, in: .circle, fallback: .thinMaterial)
                .shadow(color: .black.opacity(0.1), radius: 8, y: 2)
                .contentShape(Circle())
                .transition(.scale.combined(with: .opacity))
            } else if shouldShowPlayerPill {
                Color.clear.frame(width: scrollButtonWidth, height: scrollButtonWidth)
            }
        }
    }

    // MARK: - Video Info

    private func videoInfo(_ video: Video) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            // Title - full width (shows DeArrow title if available)
            Text(displayTitle(for: video))
                .font(.title3)
                .fontWeight(.semibold)
                .lineLimit(3)

            // Stats row - only show for non-media-source videos
            if !video.isFromMediaSource {
                VideoStatsRow(
                    playerState: playerState,
                    showFormattedDate: $showFormattedDate,
                    returnYouTubeDislikeEnabled: settingsManager?.returnYouTubeDislikeEnabled ?? false
                )
            }

            // Channel row with context menu
            VideoChannelRow(
                author: video.author,
                source: video.id.source,
                yatteeServerURL: yatteeServerURL,
                onChannelTap: onChannelTap,
                video: video,
                accentColor: accentColor,
                showSubscriberCount: !video.isFromMediaSource,
                isLoadingDetails: playerState?.videoDetailsState == .loading
            )
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
    }

    // MARK: - Info Tab Section

    private func infoTabSection(_ video: Video) -> some View {
        let hasDescription = !(video.description ?? "").isEmpty

        return VStack(alignment: .leading, spacing: 12) {
            descriptionContent(video.description ?? "")

            // Reduced bottom spacer when no description
            Spacer()
                .frame(height: hasDescription ? 80 : 50)
        }
        .padding(.vertical)
    }

    private func expandComments() {
        withAnimation(.smooth(duration: 0.3)) {
            isCommentsExpanded = true
        }
    }

    private func collapseComments() {
        appEnvironment?.navigationCoordinator.commentsFrame = .zero
        withAnimation(.smooth(duration: 0.3)) {
            isCommentsExpanded = false
        }
    }

    private func handleCommentsDismissGestureEnded(_ finalOffset: CGFloat) {
        let dismissThreshold: CGFloat = 30
        if finalOffset >= dismissThreshold {
            collapseComments()
        }
    }

    private func descriptionContent(_ description: String) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            if !description.isEmpty {
                Text(DescriptionText.attributed(description, linkColor: accentColor))
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .tint(accentColor)
                    .padding(.horizontal)
                    .handleTimestampLinks(using: playerService)
            } else if videoDetailsState == .loading {
                HStack {
                    Spacer()
                    ProgressView()
                    Spacer()
                }
                .padding()
            }
            // No description text removed - let panel shrink instead
        }
    }
}

#endif
