import Foundation
import WebRTC
import AVFoundation
import CoreMedia
#if canImport(UIKit)
import UIKit
#endif

protocol WebRTCClientDelegate: AnyObject {
    func webRTCClient(_ client: WebRTCService, didDiscoverLocalCandidate candidate: RTCIceCandidate)
    func webRTCClient(_ client: WebRTCService, didChangeConnectionState state: RTCIceConnectionState)
    func webRTCClient(_ client: WebRTCService, didReceiveData data: Data)
    func webRTCClient(_ client: WebRTCService, didReceiveRemoteVideoTrack rtcVideoTrack: RTCVideoTrack)
    func webRTCClient(_ client: WebRTCService, didCreateLocalVideoTrack rtcVideoTrack: RTCVideoTrack)
}

class WebRTCService: NSObject {
    
    // MARK: - Properties
    weak var delegate: WebRTCClientDelegate?
    private let factory: RTCPeerConnectionFactory
    private var peerConnection: RTCPeerConnection?
    private var videoCapturer: RTCVideoCapturer? // Keep reference to prevent deallocation
    private var videoSource: RTCVideoSource? // Keep reference to ensure delegate remains valid

    override init() {
        RTCInitializeSSL()
        let videoEncoderFactory = RTCDefaultVideoEncoderFactory()
        let videoDecoderFactory = RTCDefaultVideoDecoderFactory()
        self.factory = RTCPeerConnectionFactory(encoderFactory: videoEncoderFactory, decoderFactory: videoDecoderFactory)
        
        // Fix Audio Session with Robust Activation
        // Fix Audio Session with Robust Activation
        let audioSession = RTCAudioSession.sharedInstance()
        audioSession.lockForConfiguration()
        do {
            try audioSession.setCategory(AVAudioSession.Category.playAndRecord)
            try audioSession.setMode(AVAudioSession.Mode.videoChat)
            try audioSession.setActive(true)
        } catch {
            print("[WebRTC] AudioSession Configuration Error: \(error)")
        }
        audioSession.unlockForConfiguration()
    }
    
    // MARK: - Signalling
    func createOffer(completion: @escaping (RTCSessionDescription) -> Void) {
        let constraints = RTCMediaConstraints(mandatoryConstraints: [
            "OfferToReceiveAudio": "true",
            "OfferToReceiveVideo": "true"
        ], optionalConstraints: nil)
        
        self.peerConnection?.offer(for: constraints, completionHandler: { (sdp, error) in
            if let error = error {
                print("[WebRTC] Failed to create offer: \(error)")
                return
            }
            guard let sdp = sdp else { return }
            print("[WebRTC] Created Offer")
            self.peerConnection?.setLocalDescription(sdp, completionHandler: { (error) in
                if let error = error { print("[WebRTC] Failed to set local offer: \(error)") }
                completion(sdp)
            })
        })
    }
    
    func createAnswer(completion: @escaping (RTCSessionDescription) -> Void) {
        let constraints = RTCMediaConstraints(mandatoryConstraints: [
            "OfferToReceiveAudio": "true",
            "OfferToReceiveVideo": "true"
        ], optionalConstraints: nil)
        
        self.peerConnection?.answer(for: constraints, completionHandler: { (sdp, error) in
            if let error = error {
                print("[WebRTC] Failed to create answer: \(error)")
                return
            }
            guard let sdp = sdp else { return }
            print("[WebRTC] Created Answer")
            self.peerConnection?.setLocalDescription(sdp, completionHandler: { (error) in
                if let error = error { print("[WebRTC] Failed to set local answer: \(error)") }
                completion(sdp)
            })
        })
    }
    
    func setRemoteDescription(sdp: RTCSessionDescription, completion: @escaping (Error?) -> Void) {
        print("[WebRTC] Setting Remote Description: \(sdp.type == .offer ? "OFFER" : "ANSWER")")
        self.peerConnection?.setRemoteDescription(sdp, completionHandler: { error in
            if let error = error { print("[WebRTC] Remote Desc Error: \(error)") }
            completion(error)
        })
    }
    
    func setLocalDescription(sdp: RTCSessionDescription, completion: @escaping (Error?) -> Void) {
        self.peerConnection?.setLocalDescription(sdp, completionHandler: completion)
    }
    
    func addCandidate(_ candidate: RTCIceCandidate) {
        self.peerConnection?.add(candidate) { error in
            if let error = error {
                print("Failed to add ICE candidate: \(error)")
            }
        }
    }
    
    // MARK: - Connection
    func setup(turnServers: [String] = [], username: String? = nil, credential: String? = nil) {
        let config = RTCConfiguration()
        
        var iceServers = [RTCIceServer]()
        // STUN (Default Google for POC)
        iceServers.append(RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"]))
        
        // TURN (from backend)
        if !turnServers.isEmpty {
            let turnServer = RTCIceServer(urlStrings: turnServers, username: username ?? "", credential: credential ?? "")
            iceServers.append(turnServer)
        }
        
        config.iceServers = iceServers
        config.sdpSemantics = .unifiedPlan
        // config.certificate = RTCCertificate.generate(withParams: ["expires": 100000]) // Optional
        
        let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: ["DtlsSrtpKeyAgreement": "true"])
        
        self.peerConnection = self.factory.peerConnection(with: config, constraints: constraints, delegate: self)
        
        // Add local media stream
        self.startCaptureLocalVideo()
    }
    
    func disconnect() {
        self.peerConnection?.close()
        self.peerConnection = nil
        self.videoCapturer = nil
    }
    
    // MARK: - Media
    private func startCaptureLocalVideo() {
        // Create and retain video source
        let source = self.factory.videoSource()
        self.videoSource = source
        
        #if targetEnvironment(simulator)
            print("Simulator: Camera capture not supported")
        #else
            // Check Permissions
            let authStatus = AVCaptureDevice.authorizationStatus(for: .video)
            print("[WebRTC] Camera Permission Status: \(authStatus.rawValue) (3=Authorized, 0=NotDetermined, 2=Denied)")
            if authStatus != .authorized {
                print("[WebRTC] ⚠️ WARNING: Camera access not authorized!")
            }

            let capturer = RTCCameraVideoCapturer(delegate: source)
            self.videoCapturer = capturer
            
            // CRITICAL FIX: The session default preset locks the format.
            // iPad Pros often require High preset and reject VGA.
            // iPad Minis might reject High and crash on InputPriority.
            // We must set the preset AFTER startCapture because startCapture resets it to InputPriority.
             capturer.captureSession.beginConfiguration()
             // Just commit what we have (likely InputPriority default) to clear state if needed?
             // Actually, do nothing here. Let startCapture do its thing, then override.
             capturer.captureSession.commitConfiguration()

            // Observe Session Errors
            NotificationCenter.default.addObserver(forName: .AVCaptureSessionRuntimeError, object: capturer.captureSession, queue: .main) { notification in
                let error = notification.userInfo?[AVCaptureSessionErrorKey] as? Error
                print("[WebRTC] ❌ Capture Session Runtime Error: \(String(describing: error))")
            }
            
            NotificationCenter.default.addObserver(forName: .AVCaptureSessionWasInterrupted, object: capturer.captureSession, queue: .main) { notification in
                let reason = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as? Int
                print("[WebRTC] ⚠️ Capture Session Interrupted. Reason Code: \(String(describing: reason))")
            }
            
            // Find front camera
            let devices = RTCCameraVideoCapturer.captureDevices()
            if let frontCamera = devices.first(where: { $0.position == .front }) {
                
                let formats = RTCCameraVideoCapturer.supportedFormats(for: frontCamera)
                let targetWidth: Int32 = 1920 // Target 1080p
                let targetHeight: Int32 = 1080
                let targetFps: Float64 = 30.0
                
                var selectedFormat: AVCaptureDevice.Format?
                var currentDiff = Int32.max
                
                for format in formats {
                    let dimension = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
                    let diff = abs(dimension.width - targetWidth) + abs(dimension.height - targetHeight)
                    
                    // Verify FPS support
                    var supportsFps = false
                    for range in format.videoSupportedFrameRateRanges {
                        if range.minFrameRate <= targetFps && range.maxFrameRate >= targetFps {
                            supportsFps = true
                            break
                        }
                    }
                    
                    // On iPad Mini, 1280x960 (4:3) crashes with InputPriority (and even High in some cases).
                    // We prefer 16:9 formats (1920x1080, 1280x720) which are safer.
                    // If the found format is 4:3 but not 640x480, it might be unstable.
                    
                    let pixelCount = dimension.width * dimension.height
                    // Prefer 720p or 1080p if available
                    let isSafeRatio = (Double(dimension.width) / Double(dimension.height) > 1.7) // ~16:9
                    
                    if supportsFps && diff < currentDiff {
                        // If we are about to pick a 4:3 format >= 960p, ensure it's absolutely necessary or we prefer a safer one?
                        // Better heuristic: Just strictly target 720p if 1080p is not found.
                        
                        selectedFormat = format
                        currentDiff = diff
                    }
                    
                    // FIX: Force override if we find exact 1280x720 or 1920x1080
                    if supportsFps {
                       if dimension.width == 1920 && dimension.height == 1080 {
                           selectedFormat = format
                           currentDiff = 0
                           break
                       }
                       if dimension.width == 1280 && dimension.height == 720 {
                           // If our current selection is 1280x960 (diff ~760), this 720p (diff ~ 640+360=1000) is technically "further" from 1080p but SAFE.
                           // Actually 1280-1920=640, 720-1080=360 -> diff 1000.
                           // 1280-1920=640, 960-1080=120 -> diff 760.
                           // The math prefers 1280x960 (closer to 1080p). We need to intervene.
                           
                           // Store 720p as a strong candidate overriding "closer" but dangerous formats
                           selectedFormat = format
                           currentDiff = 0 // "Fake" perfect match to stop searching or prefer this
                       }
                    }
                }
                
                if let format = selectedFormat {
                    DispatchQueue.main.async {
                        capturer.startCapture(with: frontCamera, format: format, fps: Int(targetFps))
                        
                        // Force Preset Override for iPad Mini / Pro stability
                        // WebRTC defaults to InputPriority, which crashes some iPads with specific formats.
                        capturer.captureSession.beginConfiguration()
                        let targetPresets: [AVCaptureSession.Preset] = [.high, .medium, .low]
                        for preset in targetPresets {
                            if capturer.captureSession.canSetSessionPreset(preset) {
                                capturer.captureSession.sessionPreset = preset
                                print("[WebRTC] Post-Capture Override: Set Session Preset to \(preset.rawValue)")
                                break
                            }
                        }
                        capturer.captureSession.commitConfiguration()
                        
                        // Adapt output to save bandwidth AFTER capture starts (WebRTC scaling)
                        // Adapt output to save bandwidth AFTER capture starts (WebRTC scaling)
                        var targetWidth: Int32 = 1280
                        var targetHeight: Int32 = 720
                        
                        #if canImport(UIKit)
                        if UIDevice.current.userInterfaceIdiom == .pad {
                            let bounds = UIScreen.main.nativeBounds
                            let width = CGFloat(bounds.width)
                            let height = CGFloat(bounds.height)
                            // Determine aspect ratio based on physical pixels (usually portrait orientation for nativeBounds)
                            let maxDim = max(width, height)
                            let minDim = min(width, height)
                            let ratio = maxDim / minDim
                            
                            // "Feed the beast" - Maintain higher quality/correct aspect for iPads
                            // Use 1080 as the base height (or similar) to keep quality high
                            // Example: 4:3 -> 1440x1080
                            targetHeight = 1080
                            targetWidth = Int32(CGFloat(targetHeight) * ratio)
                            
                            // Ensure even dimensions
                            if targetWidth % 2 != 0 { targetWidth += 1 }
                            
                            print("[WebRTC] iPad detected. Adapted output to: \(targetWidth)x\(targetHeight) for ratio \(ratio)")
                        }
                        #endif
                        
                        self.videoSource?.adaptOutputFormat(toWidth: targetWidth, height: targetHeight, fps: 30)
                        
                        print("[WebRTC] Capture started: \(CMVideoFormatDescriptionGetDimensions(format.formatDescription)) @ \(targetFps)fps")
                    }
                } else if let fallback = formats.last {
                     // Last resort
                     DispatchQueue.main.async {
                         capturer.startCapture(with: frontCamera, format: fallback, fps: 30)
                         print("[WebRTC] Requested Capture at Fallback: \(CMVideoFormatDescriptionGetDimensions(fallback.formatDescription))")
                     }
                }
            } else {
                print("No front camera found")
            }
        #endif

        guard let source = self.videoSource else { return }
        let videoTrack = self.factory.videoTrack(with: source, trackId: "video0")
        
        self.peerConnection?.add(videoTrack, streamIds: ["stream0"])
        
        // Notify delegate to show local video
        self.delegate?.webRTCClient(self, didCreateLocalVideoTrack: videoTrack)
    }
}

// MARK: - RTCPeerConnectionDelegate
extension WebRTCService: RTCPeerConnectionDelegate {
    func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {}
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {}
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {}
    
    func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {}
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
        self.delegate?.webRTCClient(self, didChangeConnectionState: newState)
    }
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {}
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
        self.delegate?.webRTCClient(self, didDiscoverLocalCandidate: candidate)
    }
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {}
    
    func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {}
    
    // Modern Unified Plan uses Track events
    func peerConnection(_ peerConnection: RTCPeerConnection, didStartReceivingOn transceiver: RTCRtpTransceiver) {
        print("[WebRTC] didStartReceivingOn transceiver: \(transceiver.mediaType == .video ? "Video" : "Audio")")
        if transceiver.mediaType == .video, let track = transceiver.receiver.track as? RTCVideoTrack {
             print("[WebRTC] Received Remote Video Track!")
             
             self.delegate?.webRTCClient(self, didReceiveRemoteVideoTrack: track)
        }
    }
}
