//
//  MPVLogging.swift
//  Yattee
//
//  Centralized MPV rendering diagnostic logging.
//  Logs to Console (print) AND LoggingService for persistence.
//

import Foundation
#if os(iOS) || os(tvOS)
import OpenGLES
#elseif os(macOS)
import OpenGL
#endif

/// Centralized MPV rendering diagnostic logging.
/// Use this to diagnose rare rendering issues (black/green screen while audio plays).
enum MPVLogging {
    // MARK: - Setting Check

    /// Thread-safe cached check for verbose logging setting.
    /// Uses atomic operations for thread safety without locks.
    private static var _cachedIsEnabled: Bool = false
    private static var _lastCheckTime: UInt64 = 0
    private static let cacheDurationNanos: UInt64 = 1_000_000_000 // 1 second

    /// Whether verbose MPV logging is enabled (cached, safe from any thread).
    /// Exposed for callers that gate their own logging on the same setting.
    static var verboseEnabled: Bool { isEnabled() }

    /// Check if verbose logging is enabled (cached for performance).
    /// Safe to call from any thread.
    private static func isEnabled() -> Bool {
        let now = DispatchTime.now().uptimeNanoseconds

        // Refresh cache every second
        if now - _lastCheckTime > cacheDurationNanos {
            _lastCheckTime = now
            // Read from UserDefaults directly for thread safety
            // (SettingsManager is @MainActor).
            // The master "Enable Logging" switch takes precedence: with it off,
            // verbose MPV logging is off no matter what the verbose flag says.
            _cachedIsEnabled = UserDefaults.standard.bool(forKey: "loggingEnabled")
                && UserDefaults.standard.bool(forKey: "verboseMPVLogging")
        }

        return _cachedIsEnabled
    }

    // MARK: - Logging Functions

    /// Log a verbose MPV rendering diagnostic message.
    /// Only logs if verbose MPV logging is enabled in settings.
    /// Thread-safe and can be called from any queue.
    ///
    /// - Parameters:
    ///   - message: The main log message
    ///   - details: Optional additional details
    ///   - file: Source file (auto-captured)
    ///   - function: Function name (auto-captured)
    ///   - line: Line number (auto-captured)
    static func log(
        _ message: String,
        details: String? = nil,
        file: String = #file,
        function: String = #function,
        line: Int = #line
    ) {
        guard isEnabled() else { return }

        let timestamp = Self.timestamp()
        let threadName = Self.threadName()
        let fileName = (file as NSString).lastPathComponent

        let fullMessage = "[MPV-Verbose] [\(timestamp)] [\(threadName)] \(message)"

        // Log to Console immediately (thread-safe)
        print(fullMessage)
        if let details {
            print("  \(details)")
        }
        print("  [\(fileName):\(line) \(function)]")

        // Log to LoggingService on MainActor for persistence
        let logDetails = details.map { "\($0)\n[\(fileName):\(line) \(function)]" }
            ?? "[\(fileName):\(line) \(function)]"

        Task { @MainActor in
            LoggingService.shared.log(
                level: .debug,
                category: .mpv,
                message: "[MPV-Verbose] \(message)",
                details: logDetails
            )
        }
    }

    /// Log with warning level for potential issues.
    static func warn(
        _ message: String,
        details: String? = nil,
        file: String = #file,
        function: String = #function,
        line: Int = #line
    ) {
        guard isEnabled() else { return }

        let timestamp = Self.timestamp()
        let threadName = Self.threadName()
        let fileName = (file as NSString).lastPathComponent

        let fullMessage = "[MPV-Verbose] ⚠️ \(timestamp)] [\(threadName)] \(message)"

        print(fullMessage)
        if let details {
            print("  \(details)")
        }
        print("  [\(fileName):\(line) \(function)]")

        let logDetails = details.map { "\($0)\n[\(fileName):\(line) \(function)]" }
            ?? "[\(fileName):\(line) \(function)]"

        Task { @MainActor in
            LoggingService.shared.log(
                level: .warning,
                category: .mpv,
                message: "[MPV-Verbose] \(message)",
                details: logDetails
            )
        }
    }

    /// Log OpenGL/EAGL state for debugging context and framebuffer issues.
    ///
    /// - Parameters:
    ///   - prefix: Description of the operation (e.g., "createFramebuffer")
    ///   - framebuffer: The framebuffer ID
    ///   - renderbuffer: The renderbuffer ID
    ///   - width: Framebuffer width
    ///   - height: Framebuffer height
    ///   - contextCurrent: Whether the GL context is current
    ///   - framebufferComplete: Whether the framebuffer is complete (nil if not checked)
    static func logGLState(
        _ prefix: String,
        framebuffer: UInt32,
        renderbuffer: UInt32,
        width: Int32,
        height: Int32,
        contextCurrent: Bool,
        framebufferComplete: Bool? = nil
    ) {
        var state = "FB:\(framebuffer) RB:\(renderbuffer) \(width)x\(height) ctx:\(contextCurrent ? "✓" : "✗")"
        if let complete = framebufferComplete {
            state += " complete:\(complete ? "✓" : "✗")"
        }

        log("\(prefix): \(state)")
    }

    /// Log display link state changes.
    ///
    /// - Parameters:
    ///   - action: The action being performed (e.g., "start", "stop", "pause")
    ///   - isPaused: Current paused state
    ///   - targetFPS: Target frame rate if applicable
    ///   - reason: Optional reason for the action
    static func logDisplayLink(
        _ action: String,
        isPaused: Bool? = nil,
        targetFPS: Double? = nil,
        reason: String? = nil
    ) {
        var details: [String] = []
        if let isPaused {
            details.append("paused:\(isPaused)")
        }
        if let targetFPS {
            details.append("targetFPS:\(String(format: "%.1f", targetFPS))")
        }
        if let reason {
            details.append("reason:\(reason)")
        }

        let detailsStr = details.isEmpty ? nil : details.joined(separator: " ")
        log("DisplayLink \(action)", details: detailsStr)
    }

    /// Log view lifecycle events.
    ///
    /// - Parameters:
    ///   - event: The lifecycle event (e.g., "willMove(toSuperview:)", "didMoveToSuperview")
    ///   - hasSuperview: Whether the view has a superview after the event
    ///   - details: Additional context
    static func logViewLifecycle(
        _ event: String,
        hasSuperview: Bool,
        details: String? = nil
    ) {
        log("View \(event)", details: "hasSuperview:\(hasSuperview)" + (details.map { " \($0)" } ?? ""))
    }

    /// Log app lifecycle / scene phase transitions.
    ///
    /// - Parameters:
    ///   - event: The lifecycle event
    ///   - isPiPActive: Whether PiP is currently active
    ///   - isRendering: Whether rendering is active
    static func logAppLifecycle(
        _ event: String,
        isPiPActive: Bool? = nil,
        isRendering: Bool? = nil
    ) {
        var details: [String] = []
        if let isPiPActive {
            details.append("pip:\(isPiPActive)")
        }
        if let isRendering {
            details.append("rendering:\(isRendering)")
        }

        let detailsStr = details.isEmpty ? nil : details.joined(separator: " ")
        log("App \(event)", details: detailsStr)
    }

    /// Log rotation and fullscreen transitions.
    ///
    /// - Parameters:
    ///   - event: The transition event
    ///   - fromOrientation: Previous orientation if applicable
    ///   - toOrientation: Target orientation if applicable
    static func logTransition(
        _ event: String,
        fromSize: CGSize? = nil,
        toSize: CGSize? = nil
    ) {
        var details: [String] = []
        if let fromSize {
            details.append("from:\(Int(fromSize.width))x\(Int(fromSize.height))")
        }
        if let toSize {
            details.append("to:\(Int(toSize.width))x\(Int(toSize.height))")
        }

        let detailsStr = details.isEmpty ? nil : details.joined(separator: " ")
        log("Transition \(event)", details: detailsStr)
    }

    /// Log render operations (use sparingly to avoid log spam).
    ///
    /// - Parameters:
    ///   - event: The render event
    ///   - fbo: Framebuffer being rendered to
    ///   - width: Render width
    ///   - height: Render height
    ///   - success: Whether the operation succeeded
    static func logRender(
        _ event: String,
        fbo: Int32? = nil,
        width: Int32? = nil,
        height: Int32? = nil,
        success: Bool? = nil
    ) {
        var details: [String] = []
        if let fbo {
            details.append("fbo:\(fbo)")
        }
        if let width, let height {
            details.append("\(width)x\(height)")
        }
        if let success {
            details.append(success ? "✓" : "✗")
        }

        let detailsStr = details.isEmpty ? nil : details.joined(separator: " ")
        log("Render \(event)", details: detailsStr)
    }

    // MARK: - Private Helpers

    private static func timestamp() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss.SSS"
        return formatter.string(from: Date())
    }

    private static func threadName() -> String {
        if Thread.isMainThread {
            return "main"
        }
        if let name = Thread.current.name, !name.isEmpty {
            return name
        }
        // Get queue label if available
        let label = String(cString: __dispatch_queue_get_label(nil), encoding: .utf8) ?? "unknown"
        return label
    }
}
