import UIKit
import TVVLCKit

/**
 VLC Player View for tvOS

 KNOWN ISSUE:
 VLC's internal OpenGL rendering engine (VLCOpenGLES2VideoView) may generate
 "Modifying properties of a view's layer off the main thread" warnings.

 This is a VLC framework internal behavior and does not affect functionality.
 The warnings originate from VLC's OpenGL buffer management during video decoding
 and are harmless in practice. VLC manages its own threading for video rendering
 and this warning can be safely ignored.

 Reference: VLCOpenGLES2VideoView.doResetBuffers in TVVLCKit framework
 */
class VLCPlayerView: UIView, VLCMediaDelegate {
    var mediaPlayer: VLCMediaPlayer!
    private var observers: [NSObjectProtocol] = []
    private var currentMedia: VLCMedia?
    private var progressTimer: Timer?
    var onStateChange: ((VLCMediaPlayerState) -> Void)?
    var onProgressUpdate: ((TimeInterval, TimeInterval) -> Void)?

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

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

    private func setupPlayer() {
        // Initialize VLC media player on main thread to avoid UIKit violations
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }

            self.mediaPlayer = VLCMediaPlayer()

            // Set drawable on main thread - critical for UIKit compliance
            self.mediaPlayer.drawable = self

            // Configure player for optimal tvOS playback
            self.mediaPlayer.scaleFactor = 0

            print("🎥 VLC Media Player initialized")
        }
    }

    func loadMedia(from url: URL) {
        print("🎥 VLC loading media from URL: \(url)")

        // Create VLC media from URL
        let media = VLCMedia(url: url)
        currentMedia = media

        // Set delegate to receive parsing callbacks
        media.delegate = self

        // Configure media options for better network streaming with reduced verbosity
        media.addOption("--network-caching=3000")
        media.addOption("--file-caching=3000")
        media.addOption("--quiet")  // Suppress all console output except errors
        media.addOption("--http-reconnect")
        media.addOption("--avcodec-hw=any")  // Enable hardware acceleration on device
        media.addOption("--mkv-use-ordered-chapters=0")  // Disable MKV chapter linking
        media.addOption("--mkv-preload-local-dir=0")  // Skip local directory preloading
        media.addOption("--mkv-seek-percent")  // Use percentage-based seeking
        media.addOption("--no-avcodec-dr")  // Disable direct rendering to avoid threading issues
        media.addOption("--vout=ios_window")  // Use iOS window output for better thread safety

        // Ensure media player operations happen on main thread
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }

            self.mediaPlayer.media = media

            print("🎥 Starting asynchronous media parsing...")
            media.parse()

            // Add state change observer
            let stateObserver = NotificationCenter.default.addObserver(
                forName: NSNotification.Name(rawValue: VLCMediaPlayerStateChanged),
                object: self.mediaPlayer,
                queue: .main
            ) { [weak self] notification in
                self?.playerStateChanged()
            }
            self.observers.append(stateObserver)

            // Add media state observer for more detailed error information
            let mediaStateObserver = NotificationCenter.default.addObserver(
                forName: NSNotification.Name(rawValue: VLCMediaMetaChanged),
                object: media,
                queue: .main
            ) { notification in
                print("🎥 VLC Media meta changed")
            }
            self.observers.append(mediaStateObserver)
        }
    }

    func play() {
        print("▶️ VLC starting playback")
        DispatchQueue.main.async { [weak self] in
            self?.mediaPlayer.play()
        }
    }

    func pause() {
        print("⏸️ VLC pausing playback")
        DispatchQueue.main.async { [weak self] in
            self?.mediaPlayer.pause()
        }
    }

    func stop() {
        print("⏹️ VLC stopping playback")
        DispatchQueue.main.async { [weak self] in
            self?.mediaPlayer.stop()
        }
    }

    private func playerStateChanged() {
        guard let state = mediaPlayer?.state else { return }

        // Notify state change callback
        onStateChange?(state)

        switch state {
        case .opening:
            print("🎥 VLC State: Opening")
        case .buffering:
            // Suppress buffering log spam
            break
        case .playing:
            print("✅ VLC State: Playing")
            startProgressTimer()
        case .paused:
            print("⏸️ VLC State: Paused")
            stopProgressTimer()
            reportProgress()
        case .stopped:
            print("⏹️ VLC State: Stopped")
            stopProgressTimer()
            reportProgress()
        case .ended:
            print("🏁 VLC State: Ended")
            stopProgressTimer()
            reportProgress()
        case .error:
            print("❌ VLC State: Error")
            stopProgressTimer()
            if let media = mediaPlayer?.media {
                print("   Failed to play: \(media.url?.absoluteString ?? "unknown URL")")
                print("   Media parsed status: \(media.parsedStatus.rawValue)")
                if let tracks = media.tracksInformation as? [[String: Any]] {
                    print("   Tracks found: \(tracks.count)")
                    for (index, track) in tracks.enumerated() {
                        print("   Track \(index): \(track)")
                    }
                } else {
                    print("   No track information available")
                }
            }
        case .esAdded:
            // Suppress elementary stream added messages
            break
        @unknown default:
            print("🎥 VLC State: Unknown (\(state.rawValue))")
        }
    }

    // MARK: - Progress Tracking

    private func startProgressTimer() {
        stopProgressTimer()
        progressTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { [weak self] _ in
            self?.reportProgress()
        }
    }

    private func stopProgressTimer() {
        progressTimer?.invalidate()
        progressTimer = nil
    }

    private func reportProgress() {
        guard let player = mediaPlayer else { return }

        let currentTime = player.time.intValue
        let duration = player.media?.length.intValue ?? 0

        guard duration > 0 else { return }

        let currentSeconds = TimeInterval(currentTime) / 1000.0
        let durationSeconds = TimeInterval(duration) / 1000.0

        onProgressUpdate?(currentSeconds, durationSeconds)
    }

    func seekTo(position: TimeInterval) {
        guard let player = mediaPlayer, let media = player.media else { return }
        let duration = media.length.intValue
        guard duration > 0 else { return }

        let positionFraction = Float(position * 1000.0) / Float(duration)
        DispatchQueue.main.async {
            player.position = positionFraction
        }
    }

    // MARK: - VLCMediaDelegate

    func mediaDidFinishParsing(_ aMedia: VLCMedia) {
        print("🎥 VLC Media parsing completed via delegate")
        print("   Parsed status: \(aMedia.parsedStatus.rawValue)")
        print("   Media URL: \(aMedia.url?.absoluteString ?? "unknown")")

        if aMedia.length.intValue > 0 {
            print("   Media length: \(aMedia.length.intValue) ms (\(aMedia.length.intValue / 1000 / 60) minutes)")
        } else {
            print("   ⚠️ Media length is 0 or unknown")
        }

        // Get track information
        let trackInfo = aMedia.tracksInformation
        print("   Tracks information type: \(type(of: trackInfo))")
        print("   Tracks information value: \(String(describing: trackInfo))")

        if let tracks = trackInfo as? [[String: Any]] {
            print("   ✅ Tracks discovered: \(tracks.count)")
            for (index, track) in tracks.enumerated() {
                print("   Track \(index): \(track)")
            }
        } else if let tracks = trackInfo as? [Any] {
            print("   Tracks array count: \(tracks.count)")
            for (index, track) in tracks.enumerated() {
                print("   Track \(index): \(track)")
            }
        } else {
            print("   ❌ No tracks discovered after parsing (trackInfo is nil or wrong type)")
        }
    }

    func mediaMetaDataDidChange(_ aMedia: VLCMedia) {
        print("🎥 VLC Media metadata changed")
    }

    deinit {
        // Stop timer before cleanup
        progressTimer?.invalidate()

        // Clean up observers on main thread
        DispatchQueue.main.async { [observers, currentMedia, mediaPlayer] in
            observers.forEach { NotificationCenter.default.removeObserver($0) }

            // Clean up delegate
            currentMedia?.delegate = nil

            // Stop playback
            mediaPlayer?.stop()

            print("🎥 VLC Media Player deallocated")
        }
    }
}
