//
//  TVPlayerProgressBar.swift
//  Yattee
//
//  Focusable progress bar for tvOS with smooth touchpad scrubbing support.
//

#if os(tvOS)
import SwiftUI
import UIKit

/// Progress bar with smooth scrubbing support for tvOS Siri Remote touchpad.
struct TVPlayerProgressBar: View {
    let currentTime: TimeInterval
    let duration: TimeInterval
    let bufferedTime: TimeInterval
    let storyboard: Storyboard?
    let chapters: [VideoChapter]
    let onSeek: (TimeInterval) -> Void
    /// Called when scrubbing state changes - parent should stop auto-hide timer when true
    var onScrubbingChanged: ((Bool) -> Void)?
    /// Whether the current stream is live
    let isLive: Bool
    /// Whether to show chapter markers on the progress bar (default: true)
    var showChapters: Bool = true
    /// SponsorBlock segments to display on the progress bar.
    var sponsorSegments: [SponsorBlockSegment] = []
    /// Settings for SponsorBlock segment display.
    var sponsorBlockSettings: SponsorBlockSegmentSettings = .default
    /// Color for the played portion of the progress bar.
    var playedColor: Color = .red

    /// Track focus state internally.
    @FocusState private var isFocused: Bool

    /// Time during active scrubbing (nil when not scrubbing).
    @State private var scrubTime: TimeInterval?

    /// Whether user is actively scrubbing.
    @State private var isScrubbing = false

    /// Accumulated pan translation for scrubbing.
    @State private var panAccumulator: CGFloat = 0

    /// The time to display (scrub time if scrubbing, else current time).
    private var displayTime: TimeInterval {
        scrubTime ?? currentTime
    }

    /// Progress as a fraction (0-1).
    private var progress: Double {
        guard duration > 0 else { return 0 }
        return min(max(displayTime / duration, 0), 1)
    }

    /// Buffered progress as a fraction (0-1).
    private var bufferedProgress: Double {
        guard duration > 0 else { return 0 }
        return min(bufferedTime / duration, 1)
    }

    var body: some View {
        ZStack {
            // Gesture capture layer (only when scrubbing)
            if isScrubbing {
                TVPanGestureView(
                    onPanChanged: { translation, velocity in
                        handlePan(translation: translation, velocity: velocity)
                    },
                    onPanEnded: {
                        handlePanEnded()
                    }
                )
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            }

            // Visual content with button for click-to-scrub (disabled for live)
            Button {
                if isScrubbing {
                    commitScrub()
                } else if !isLive {
                    enterScrubMode()
                }
            } label: {
                progressContent
            }
            .buttonStyle(TVProgressBarButtonStyle(isFocused: isFocused))
            .disabled(isLive)
        }
        .focused($isFocused)
        .onMoveCommand { direction in
            // D-pad fallback for scrubbing
            if isScrubbing {
                handleDPad(direction: direction)
            }
        }
        .onChange(of: isFocused) { _, focused in
            if !focused {
                commitScrub()
            }
        }
        .animation(.easeInOut(duration: 0.2), value: isFocused)
        .animation(.easeInOut(duration: 0.1), value: isScrubbing)
    }

    private func enterScrubMode() {
        scrubTime = currentTime
        panAccumulator = 0
        withAnimation(.easeOut(duration: 0.15)) {
            isScrubbing = true
        }
        onScrubbingChanged?(true)
    }

    private var progressContent: some View {
        VStack(spacing: 12) {
            // Progress bar (hide for live streams)
            if !isLive {
                GeometryReader { geometry in
                    ZStack(alignment: .leading) {
                        // Progress bar with chapter segments (4pt gaps for tvOS visibility)
                        SegmentedProgressBar(
                            chapters: showChapters ? chapters : [],
                            duration: duration,
                            currentTime: displayTime,
                            bufferedTime: bufferedTime,
                            height: isFocused ? (isScrubbing ? 16 : 12) : 6,
                            gapWidth: 4,
                            playedColor: isFocused ? playedColor : .white,
                            bufferedColor: .white.opacity(0.4),
                            backgroundColor: .white.opacity(0.2),
                            sponsorSegments: sponsorSegments,
                            sponsorBlockSettings: sponsorBlockSettings
                        )

                        // Scrub handle (visible when focused)
                        if isFocused {
                            Circle()
                                .fill(.white)
                                .frame(width: isScrubbing ? 32 : 24, height: isScrubbing ? 32 : 24)
                                .shadow(color: .black.opacity(0.3), radius: 4)
                                .offset(x: (geometry.size.width * progress) - (isScrubbing ? 16 : 12))
                                .animation(.easeOut(duration: 0.1), value: progress)
                        }
                    }
                }
                .frame(height: isFocused ? (isScrubbing ? 16 : 12) : 6)
            }

            // Time labels
            HStack {
                // Current time or LIVE indicator
                if isLive {
                    HStack(spacing: 6) {
                        Circle()
                            .fill(.red)
                            .frame(width: 8, height: 8)
                        Text(String(localized: "player.live"))
                            .font(.subheadline.weight(.semibold))
                            .foregroundStyle(.red)
                    }
                } else {
                    Text(formatTime(displayTime))
                        .monospacedDigit()
                        .font(.subheadline)
                        .fontWeight(isScrubbing ? .semibold : .regular)
                        .foregroundStyle(.white)
                }

                Spacer()

                // Scrub hint when focused (only for non-live)
                if !isLive && isFocused && !isScrubbing {
                    Text(String(localized: "player.tvos.scrubHint"))
                        .font(.caption)
                        .foregroundStyle(.white.opacity(0.5))
                } else if !isLive && isScrubbing {
                    Text(String(localized: "player.tvos.seekHint"))
                        .font(.caption)
                        .foregroundStyle(.white.opacity(0.7))
                }

                Spacer()

                // Remaining time (only for non-live)
                if !isLive {
                    Text("-\(formatTime(max(0, duration - displayTime)))")
                        .monospacedDigit()
                        .font(.subheadline)
                        .foregroundStyle(.white.opacity(0.7))
                }
            }

            // Scrub preview (storyboard thumbnail or large time display)
            if isScrubbing {
                if let storyboard {
                    TVSeekPreviewView(
                        storyboard: storyboard,
                        seekTime: scrubTime ?? currentTime,
                        chapters: showChapters ? chapters : []
                    )
                    .transition(.scale.combined(with: .opacity))
                } else {
                    // Fallback when no storyboard available
                    Text(formatTime(scrubTime ?? currentTime))
                        .font(.system(size: 48, weight: .medium))
                        .monospacedDigit()
                        .foregroundStyle(.white)
                        .padding(.horizontal, 32)
                        .padding(.vertical, 16)
                        .background(
                            RoundedRectangle(cornerRadius: 16)
                                .fill(.ultraThinMaterial)
                        )
                        .transition(.scale.combined(with: .opacity))
                }
            }
        }
    }

    // MARK: - Pan Gesture Handling

    private func handlePan(translation: CGFloat, velocity: CGFloat) {
        guard duration > 0, isScrubbing else { return }

        // Cancel any pending seek when user starts new pan
        seekTask?.cancel()

        // Calculate scrub sensitivity based on duration
        // Lower values = slower/finer scrubbing
        let baseSensitivity: CGFloat
        if duration > 3600 {
            baseSensitivity = duration / 2000  // ~1.8 sec per unit for 1hr video
        } else if duration > 600 {
            baseSensitivity = duration / 3000  // ~0.2 sec per unit for 10min video
        } else {
            baseSensitivity = duration / 4000  // very fine control for short videos
        }

        // Apply velocity multiplier (faster swipe = faster scrub)
        // Reduced range: 0.3x to 2.0x
        let velocityMultiplier = min(max(abs(velocity) / 800, 0.3), 2.0)
        let adjustedSensitivity = baseSensitivity * velocityMultiplier

        // Update scrub time based on translation delta
        let delta = translation - panAccumulator
        panAccumulator = translation

        let timeChange = TimeInterval(delta * adjustedSensitivity)
        let currentScrubTime = scrubTime ?? currentTime
        scrubTime = min(max(0, currentScrubTime + timeChange), duration)
    }

    private func handlePanEnded() {
        // Reset accumulator for next swipe
        panAccumulator = 0
        // Schedule debounced seek but stay in scrub mode
        scheduleSeek()
    }

    @State private var seekTask: Task<Void, Never>?

    private func scheduleSeek() {
        seekTask?.cancel()
        seekTask = Task {
            try? await Task.sleep(for: .milliseconds(1000))
            guard !Task.isCancelled else { return }
            await MainActor.run {
                if let time = scrubTime {
                    onSeek(time)
                }
            }
        }
    }

    // MARK: - D-Pad Fallback

    private func handleDPad(direction: MoveCommandDirection) {
        guard duration > 0, isScrubbing else { return }

        switch direction {
        case .left, .right:
            // Determine scrub increment based on video length
            let scrubAmount: TimeInterval
            if duration > 3600 {
                scrubAmount = 30
            } else if duration > 600 {
                scrubAmount = 15
            } else {
                scrubAmount = 10
            }

            let currentScrubTime = scrubTime ?? currentTime
            if direction == .left {
                scrubTime = max(0, currentScrubTime - scrubAmount)
            } else {
                scrubTime = min(duration, currentScrubTime + scrubAmount)
            }

        case .up, .down:
            // Exit scrub mode and let navigation happen
            commitScrub()

        @unknown default:
            break
        }
    }

    // MARK: - Commit

    private func commitScrub() {
        seekTask?.cancel()
        seekTask = nil

        let wasScrubbing = isScrubbing

        if let time = scrubTime {
            onSeek(time)
        }

        withAnimation(.easeOut(duration: 0.15)) {
            scrubTime = nil
            isScrubbing = false
        }
        panAccumulator = 0

        if wasScrubbing {
            onScrubbingChanged?(false)
        }
    }

    // MARK: - Formatting

    private func formatTime(_ time: TimeInterval) -> String {
        let totalSeconds = Int(max(0, time))
        let hours = totalSeconds / 3600
        let minutes = (totalSeconds % 3600) / 60
        let seconds = totalSeconds % 60

        if hours > 0 {
            return String(format: "%d:%02d:%02d", hours, minutes, seconds)
        }
        return String(format: "%d:%02d", minutes, seconds)
    }
}

// MARK: - Pan Gesture View

/// UIKit view that captures pan gestures on the Siri Remote touchpad.
struct TVPanGestureView: UIViewRepresentable {
    let onPanChanged: (CGFloat, CGFloat) -> Void  // (translation, velocity)
    let onPanEnded: () -> Void

    func makeUIView(context: Context) -> TVPanGestureUIView {
        let view = TVPanGestureUIView()
        view.onPanChanged = onPanChanged
        view.onPanEnded = onPanEnded
        return view
    }

    func updateUIView(_ uiView: TVPanGestureUIView, context: Context) {
        uiView.onPanChanged = onPanChanged
        uiView.onPanEnded = onPanEnded
    }
}

class TVPanGestureUIView: UIView {
    var onPanChanged: ((CGFloat, CGFloat) -> Void)?
    var onPanEnded: (() -> Void)?

    private var panRecognizer: UIPanGestureRecognizer!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupGesture()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupGesture()
    }

    private func setupGesture() {
        panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        panRecognizer.allowedTouchTypes = [NSNumber(value: UITouch.TouchType.indirect.rawValue)]
        addGestureRecognizer(panRecognizer)

        // Make view focusable
        isUserInteractionEnabled = true
    }

    @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: self).x
        let velocity = gesture.velocity(in: self).x

        switch gesture.state {
        case .began, .changed:
            onPanChanged?(translation, velocity)
        case .ended, .cancelled:
            onPanEnded?()
            gesture.setTranslation(.zero, in: self)
        default:
            break
        }
    }
}

// MARK: - Button Style

/// Button style for the progress bar.
struct TVProgressBarButtonStyle: ButtonStyle {
    let isFocused: Bool

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
            .opacity(configuration.isPressed ? 0.9 : 1.0)
    }
}

#endif
