//
//  QualitySelectorView.swift
//  Yattee
//
//  Sheet for selecting video quality, audio track, and subtitles.
//

import SwiftUI

struct QualitySelectorView: View {
    // MARK: - Environment

    @Environment(\.dismiss) var dismiss
    @Environment(\.appEnvironment) var appEnvironment

    // MARK: - Properties

    let streams: [Stream]
    let captions: [Caption]
    let currentStream: Stream?
    let currentAudioStream: Stream?
    let currentCaption: Caption?
    let isLoading: Bool
    let currentDownload: Download?
    let isLoadingOnlineStreams: Bool
    let localCaptionURL: URL?
    let onStreamSelected: (Stream, Stream?) -> Void
    let onCaptionSelected: (Caption?) -> Void
    let onLoadOnlineStreams: () -> Void
    let onSwitchToOnlineStream: (Stream, Stream?) -> Void

    /// Embedded (in-container) tracks reported by mpv for the loaded file.
    let embeddedAudioTracks: [MPVTrack]
    let embeddedSubtitleTracks: [MPVTrack]
    let currentEmbeddedAudioTrackID: Int?
    let currentEmbeddedSubtitleTrackID: Int?
    /// The playing file's video track, used to label metadata-less local streams
    let embeddedVideoTrack: MPVTrack?
    let onEmbeddedAudioTrackSelected: (Int) -> Void
    /// nil = subtitles off
    let onEmbeddedSubtitleTrackSelected: (Int?) -> Void

    /// Current playback rate
    var currentRate: PlaybackRate = .x1
    /// Callback when playback rate changes
    var onRateChanged: ((PlaybackRate) -> Void)?

    /// Whether controls are locked
    var isControlsLocked: Bool = false
    /// Callback when lock state changes
    var onLockToggled: ((Bool) -> Void)?

    /// Whether audio-only (music) mode is enabled
    var isAudioMode: Bool = false
    /// Callback when audio mode is toggled
    var onAudioModeToggled: ((Bool) -> Void)?

    /// Initial tab to show when view appears
    var initialTab: QualitySelectorTab = .video
    /// Whether to show the segmented tab picker (false for focused single-tab mode)
    var showTabPicker: Bool = true

    /// Optional dismiss callback used when the view is presented inline (e.g. as
    /// the tvOS half-screen panel). When nil, falls back to `@Environment(\.dismiss)`.
    var onDismiss: (() -> Void)?

    #if os(tvOS)
    /// Bound to the first focusable row so we can programmatically pull focus
    /// into the panel on appear (the system doesn't auto-focus an inline
    /// overlay the way it does for `fullScreenCover`).
    @FocusState var inlinePanelInitialFocus: Bool

    /// Tracks pushed destinations so the Menu-button handler can pop instead
    /// of dismissing when the user has navigated into a detail screen.
    @State private var navigationPath = NavigationPath()
    #endif

    // MARK: - State
    @State var selectedTab: QualitySelectorTab = .video
    @State var selectedVideoStream: Stream?
    @State var selectedAudioStream: Stream?
    #if os(iOS)
    @State var showingSubtitleFilePicker = false
    #endif

    // MARK: - Settings Access

    /// The preferred audio language from settings
    var preferredAudioLanguage: String? {
        appEnvironment?.settingsManager.preferredAudioLanguage
    }

    /// The preferred subtitles language from settings
    var preferredSubtitlesLanguage: String? {
        appEnvironment?.settingsManager.preferredSubtitlesLanguage
    }

    /// The preferred video quality from settings
    var preferredQuality: VideoQuality {
        appEnvironment?.settingsManager.preferredQuality ?? .auto
    }

    /// Whether to show advanced stream details (codec, bitrate, size)
    var showAdvancedStreamDetails: Bool {
        appEnvironment?.settingsManager.showAdvancedStreamDetails ?? false
    }

    var allowSoftwareDecodedFormats: Bool {
        appEnvironment?.settingsManager.allowSoftwareDecodedFormats ?? false
    }

    // MARK: - Computed Properties

    /// Available tabs based on streams
    var availableTabs: [QualitySelectorTab] {
        var tabs: [QualitySelectorTab] = [.video]
        if (hasVideoOnlyStreams && !audioStreams.isEmpty) || embeddedAudioTracks.count > 1 {
            tabs.append(.audio)
        }
        if !captions.isEmpty || !embeddedSubtitleTracks.isEmpty || canLoadExternalSubtitles {
            tabs.append(.subtitles)
        }
        return tabs
    }

    /// Whether the "Load subtitle from file…" row is available: media-source
    /// playback (local folder/SMB/WebDAV) on platforms with a file picker.
    var canLoadExternalSubtitles: Bool {
        #if os(tvOS)
        return false
        #else
        return appEnvironment?.playerService.state.currentVideo?.isFromMediaSource == true
        #endif
    }

    /// Navigation title based on mode
    var navigationTitle: String {
        if showTabPicker {
            return String(localized: "player.quality.settings")
        } else {
            switch initialTab {
            case .video:
                return String(localized: "player.quality.video")
            case .audio:
                return String(localized: "stream.audio")
            case .subtitles:
                return String(localized: "stream.subtitles")
            }
        }
    }

    /// Whether we're playing downloaded content
    var isPlayingDownloadedContent: Bool {
        currentDownload != nil
    }

    /// Whether streams are empty (not loading, but no streams available)
    var hasNoStreams: Bool {
        if !showTabPicker && initialTab == .subtitles {
            return !isLoading && captions.isEmpty && embeddedSubtitleTracks.isEmpty
                && !isPlayingDownloadedContent && !canLoadExternalSubtitles
        }
        return !isLoading && streams.isEmpty && !isPlayingDownloadedContent
    }

    /// Whether online streams have been loaded
    var hasOnlineStreams: Bool {
        streams.contains { !$0.url.isFileURL }
    }

    /// Whether the current or selected video stream is muxed
    var isCurrentStreamMuxed: Bool {
        if let selected = selectedVideoStream {
            return selected.isMuxed
        }
        return currentStream?.isMuxed ?? false
    }

    // MARK: - Initialization

    init(
        streams: [Stream],
        captions: [Caption] = [],
        currentStream: Stream?,
        currentAudioStream: Stream? = nil,
        currentCaption: Caption? = nil,
        isLoading: Bool = false,
        currentDownload: Download? = nil,
        isLoadingOnlineStreams: Bool = false,
        localCaptionURL: URL? = nil,
        currentRate: PlaybackRate = .x1,
        isControlsLocked: Bool = false,
        isAudioMode: Bool = false,
        initialTab: QualitySelectorTab = .video,
        showTabPicker: Bool = true,
        embeddedAudioTracks: [MPVTrack] = [],
        embeddedSubtitleTracks: [MPVTrack] = [],
        currentEmbeddedAudioTrackID: Int? = nil,
        currentEmbeddedSubtitleTrackID: Int? = nil,
        embeddedVideoTrack: MPVTrack? = nil,
        onStreamSelected: @escaping (Stream, Stream?) -> Void,
        onCaptionSelected: @escaping (Caption?) -> Void = { _ in },
        onLoadOnlineStreams: @escaping () -> Void = {},
        onSwitchToOnlineStream: @escaping (Stream, Stream?) -> Void = { _, _ in },
        onEmbeddedAudioTrackSelected: @escaping (Int) -> Void = { _ in },
        onEmbeddedSubtitleTrackSelected: @escaping (Int?) -> Void = { _ in },
        onRateChanged: ((PlaybackRate) -> Void)? = nil,
        onLockToggled: ((Bool) -> Void)? = nil,
        onAudioModeToggled: ((Bool) -> Void)? = nil,
        onDismiss: (() -> Void)? = nil
    ) {
        self.streams = streams
        self.captions = captions
        self.currentStream = currentStream
        self.currentAudioStream = currentAudioStream
        self.currentCaption = currentCaption
        self.isLoading = isLoading
        self.currentDownload = currentDownload
        self.isLoadingOnlineStreams = isLoadingOnlineStreams
        self.localCaptionURL = localCaptionURL
        self.initialTab = initialTab
        self.showTabPicker = showTabPicker
        self.currentRate = currentRate
        self.isControlsLocked = isControlsLocked
        self.isAudioMode = isAudioMode
        self.embeddedAudioTracks = embeddedAudioTracks
        self.embeddedSubtitleTracks = embeddedSubtitleTracks
        self.currentEmbeddedAudioTrackID = currentEmbeddedAudioTrackID
        self.currentEmbeddedSubtitleTrackID = currentEmbeddedSubtitleTrackID
        self.embeddedVideoTrack = embeddedVideoTrack
        self.onStreamSelected = onStreamSelected
        self.onCaptionSelected = onCaptionSelected
        self.onLoadOnlineStreams = onLoadOnlineStreams
        self.onSwitchToOnlineStream = onSwitchToOnlineStream
        self.onEmbeddedAudioTrackSelected = onEmbeddedAudioTrackSelected
        self.onEmbeddedSubtitleTrackSelected = onEmbeddedSubtitleTrackSelected
        self.onRateChanged = onRateChanged
        self.onLockToggled = onLockToggled
        self.onAudioModeToggled = onAudioModeToggled
        self.onDismiss = onDismiss
    }

    // MARK: - Body

    @ViewBuilder
    private var rootContent: some View {
        if isLoading {
            loadingContent
        } else if isPlayingDownloadedContent {
            downloadedContent
        } else if hasNoStreams {
            emptyContent
        } else {
            streamsContent
        }
    }

    var body: some View {
        #if os(tvOS)
        NavigationStack(path: $navigationPath) {
            stackRoot
        }
        .onExitCommand {
            // Pop the pushed detail view if present, otherwise dismiss the
            // whole panel. (Without this, Menu always falls through to
            // `performDismiss()` and closes the entire overlay even from a
            // detail screen.)
            if !navigationPath.isEmpty {
                navigationPath.removeLast()
            } else {
                performDismiss()
            }
        }
        #else
        NavigationStack {
            stackRoot
        }
        #if os(iOS)
        // Rest at a taller-than-medium detent so the general section (playback
        // speed / audio mode / lock) at the bottom of the scroll view is
        // visible without dragging the sheet up to `.large`.
        .presentationDetents([.fraction(0.6), .large])
        #endif
        #if os(macOS)
        .frame(minWidth: 450, minHeight: 500)
        #endif
        #endif
    }

    @ViewBuilder
    private var stackRoot: some View {
        Group {
            #if os(tvOS)
            // Custom title bar matches the queue panel's style.
            VStack(alignment: .leading, spacing: 0) {
                ZStack {
                    Text(navigationTitle)
                        .font(.system(size: 32, weight: .semibold))
                        .foregroundStyle(.primary)
                }
                .frame(maxWidth: .infinity)
                .padding(.horizontal, 80)
                .padding(.top, 32)
                .padding(.bottom, 16)

                rootContent
            }
            #else
            rootContent
            #endif
        }
        #if os(tvOS)
        // On tvOS the panel is presented inline as a half-screen overlay; it
        // supplies its own glass backdrop so the underlying video stays partly
        // visible while the menu is readable on top of the bright frame.
        .background(panelGlassBackground)
        // Disable the title-safe-area inset that NavigationStack would
        // otherwise apply on the trailing edge (because the panel sits at
        // the physical right edge of the screen).
        .ignoresSafeArea(.container, edges: .horizontal)
        #else
        .background(ListBackgroundStyle.grouped.color)
        .navigationTitle(navigationTitle)
        #endif
        #if os(iOS)
        .navigationBarTitleDisplayMode(.inline)
        #endif
        #if !os(tvOS)
        .toolbar {
            sheetCloseToolbarItem { performDismiss() }
        }
        #endif
        .navigationDestination(for: QualitySelectorDestination.self) { destination in
            switch destination {
            case .video:
                videoDetailContent
            case .audio:
                audioDetailContent
            case .subtitles:
                subtitlesDetailContent
            }
        }
        #if os(iOS)
        .sheet(isPresented: $showingSubtitleFilePicker) {
            SubtitleFilePickerView { url in
                handlePickedSubtitleFile(url)
            }
        }
        #endif
        .onAppear {
            selectedVideoStream = currentStream
            // In audio mode the audio track IS the main stream
            selectedAudioStream = currentStream?.isAudioOnly == true
                ? currentStream
                : (currentAudioStream ?? defaultAudioStream)
            #if os(tvOS)
            // Defer until after the slide-in transition so the focus engine
            // has finished routing focus away from the (now hidden) player
            // controls, then pull focus into the first row.
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.45) {
                inlinePanelInitialFocus = true
            }
            #endif
        }
    }

    #if os(tvOS)
    /// Reusable ultraThinMaterial backdrop applied to the panel root and to
    /// each pushed destination view so the glass remains continuous as the
    /// user navigates into Video / Audio / Subtitles detail screens.
    private var panelGlassBackground: some View {
        Rectangle()
            .fill(.ultraThinMaterial)
            .ignoresSafeArea()
    }
    #endif

    func performDismiss() {
        if let onDismiss {
            onDismiss()
        } else {
            dismiss()
        }
    }
}

// MARK: - Preview

#Preview {
    QualitySelectorView(
        streams: [.preview, .videoOnlyPreview, .audioPreview],
        captions: [.preview, .autoGeneratedPreview],
        currentStream: .preview,
        onStreamSelected: { _, _ in }
    )
}

#Preview("Loading") {
    QualitySelectorView(
        streams: [],
        currentStream: nil,
        isLoading: true,
        onStreamSelected: { _, _ in }
    )
}

#Preview("Empty") {
    QualitySelectorView(
        streams: [],
        currentStream: nil,
        onStreamSelected: { _, _ in }
    )
}
