import Foundation
import WebRTC

protocol WebRTCClientDelegate: AnyObject {
    func didObtainSdp(sdp: VoipSession)
    func didDiscoverLocalCandidate(candidate: RTCIceCandidate)
    // func webRTCClient(_ client: WebRTCClient, didChangeConnectionState state: RTCIceConnectionState)
    // func webRTCClient(_ client: WebRTCClient, didReceiveData data: Data)
    // func webRTCClient(_ client: WebRTCClient, didChangeVideoSize size: CGSize)
    // func webRTCClient(_ client: WebRTCClient, didChangeGatheringState state: RTCIceGatheringState)
    // func webRTCClientStreamStarted(_ client: WebRTCClient)
    // func printDebugInfo(_ line: String)
}

private let defaultICEs = [
    "stun:stun.l.google.com:19302",
    "stun:stun1.l.google.com:19302",
    "stun:stun2.l.google.com:19302",
    "stun:stun3.l.google.com:19302",
    "stun:stun4.l.google.com:19302"
]

final class WebRTCClient: NSObject {
    // The `RTCPeerConnectionFactory` is in charge of creating new RTCPeerConnection instances.
    // A new RTCPeerConnection should be created every new call, but the factory is shared.
    private static let factory: RTCPeerConnectionFactory = {
        RTCInitializeSSL()
        let videoEncoderFactory = RTCDefaultVideoEncoderFactory()
        let videoDecoderFactory = RTCDefaultVideoDecoderFactory()
        return RTCPeerConnectionFactory(encoderFactory: videoEncoderFactory, decoderFactory: videoDecoderFactory)
    }()
    private let mediaConstrains = [
        kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue,
        kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue
    ]

    private let peerConnection: RTCPeerConnection?
    private let config = RTCConfiguration()
    weak var delegate: WebRTCClientDelegate?

    init(servers: [RTCIceServer]) {
        #if DEBUG
        // RTCSetMinDebugLogLevel(RTCLoggingSeverity.verbose)
        #endif

        let iceServers = servers.isEmpty ? [RTCIceServer(urlStrings: defaultICEs)] : servers
        config.iceServers = iceServers
        config.tcpCandidatePolicy = .disabled
        config.candidateNetworkPolicy = .lowCost
        config.sdpSemantics = .unifiedPlan
        config.bundlePolicy = .maxCompat
        config.rtcpMuxPolicy = .require
        config.iceCandidatePoolSize = 5
        config.continualGatheringPolicy = .gatherOnce

        let constraints = RTCMediaConstraints(
            mandatoryConstraints: nil,
            optionalConstraints: [
                "DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue
            ]
        )
        peerConnection = WebRTCClient.factory.peerConnection(with: config, constraints: constraints, delegate: nil)

        super.init()
        peerConnection?.delegate = self
    }

    func start() {
        peerConnection?.offer(for: RTCMediaConstraints(mandatoryConstraints: mediaConstrains, optionalConstraints: nil)) { [weak self] sdp, err in
            if let err {
                // TODO: catch error here
                print(err)
            }
            guard let sdp else { return } // TODO: and here
            let session = VoipSession(
                type: VoipSessionType(rawValue: sdp.type.rawValue) ?? .offer,
                sdpStr: sdp.sdp
            )
            self?.delegate?.didObtainSdp(sdp: session)
        }
    }

//    // The `RTCPeerConnectionFactory` is in charge of creating new RTCPeerConnection instances.
//    // A new RTCPeerConnection should be created every new call, but the factory is shared.
//    private static let factory: RTCPeerConnectionFactory = {
//        RTCInitializeSSL()
//        let videoEncoderFactory = RTCDefaultVideoEncoderFactory()
//        let videoDecoderFactory = RTCDefaultVideoDecoderFactory()
//        return RTCPeerConnectionFactory(encoderFactory: videoEncoderFactory, decoderFactory: videoDecoderFactory)
//    }()
//
//    weak var delegate: WebRTCClientDelegate?
//    private let peerConnection: RTCPeerConnection
//    private let rtcAudioSession =  RTCAudioSession.sharedInstance()
//    private let audioQueue = DispatchQueue(label: "audio")
//    private let mediaConstrains = [kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue,
//                                   kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue]
//    private var videoCapturer: RTCVideoCapturer?
//    private var remoteVideoTrack: RTCVideoTrack?
//    private var remoteDataChannel: RTCDataChannel?
//
//    private var remoteStream: RTCMediaStream?
//    private var remoteRenderView: RTCEAGLVideoView?
//    private var renderer: RTCVideoRenderer?
//    var disableSoundOnStart: Bool = false

//    override init() {
//        fatalError("WebRTCClient:init is unavailable")
//    }
//
//    required init(iceServers: [String]) {
//        let config = RTCConfiguration()
//        config.iceServers = [RTCIceServer(urlStrings: iceServers)]
//
//        // Unified plan is more superior than planB
//        config.sdpSemantics = .unifiedPlan
//
//        // gatherContinually will let WebRTC to listen to any network changes and send any new candidates to the other client
//        config.continualGatheringPolicy = .gatherOnce
//
//        config.bundlePolicy = .maxCompat
//        config.candidateNetworkPolicy = .lowCost
//        config.iceTransportPolicy = .noHost
//        config.enableDscp = true
//        config.tcpCandidatePolicy = .disabled
//
//        let constraints = RTCMediaConstraints(mandatoryConstraints: nil,
//            optionalConstraints: ["DtlsSrtpKeyAgreement":kRTCMediaConstraintsValueTrue])
//        peerConnection = WebRTCClient.factory.peerConnection(with: config, constraints: constraints, delegate: nil)
//
//        super.init()
//        createAudioSender()
//        peerConnection.delegate = self
//    }
//
//    func freeResources() {
//        peerConnection.delegate = nil
//        remoteRenderView?.delegate = nil
//        remoteRenderView = nil
//        delegate = nil
//    }
//
//    // MARK: Signaling
//    func offer(completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
//        let constrains = RTCMediaConstraints(mandatoryConstraints: mediaConstrains,
//            optionalConstraints: nil)
//        peerConnection.offer(for: constrains) { (sdp, error) in
//            guard let sdp = sdp else {
//                return
//            }
//
//            self.peerConnection.setLocalDescription(sdp, completionHandler: { (error) in
//                completion(sdp)
//            })
//        }
//    }
//
//    func answer(completion: @escaping (_ sdp: RTCSessionDescription) -> Void)  {
//        let constrains = RTCMediaConstraints(mandatoryConstraints: mediaConstrains,
//            optionalConstraints: nil)
//        peerConnection.answer(for: constrains) { (sdp, error) in
//            if let e = error {
//                self.delegate?.printDebugInfo("sdp answer error: \(e.localizedDescription)")
//                return
//            }
//            guard let sdp = sdp else {
//                self.delegate?.printDebugInfo("no sdp in answer!")
//                return
//            }
//            self.delegate?.printDebugInfo("\(sdp)")
//
//            self.peerConnection.setLocalDescription(sdp, completionHandler: { (error) in
//                if let e = error {
//                    self.delegate?.printDebugInfo("set local description error: \(e.localizedDescription)")
//                    return
//                }
//                completion(sdp)
//            })
//        }
//    }

//    func set(remoteSdp: RTCSessionDescription, completion: @escaping (Error?) -> ()) {
//        peerConnection.setRemoteDescription(remoteSdp, completionHandler: completion)
//    }
//
//    func set(remoteCandidate: RTCIceCandidate) {
//        peerConnection.add(remoteCandidate)
//    }
//
//    func renderRemoteVideo() -> RTCEAGLVideoView? {
//        remoteRenderView = RTCEAGLVideoView()
//        remoteRenderView?.delegate = self
//        return remoteRenderView
//    }
//
//    private func createAudioSender() {
//        let streamId = "stream"
//        let audioConstrains = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
//        let audioSource = WebRTCClient.factory.audioSource(with: audioConstrains)
//        let audioTrack = WebRTCClient.factory.audioTrack(with: audioSource, trackId: "audio0")
//        peerConnection.add(audioTrack, streamIds: [streamId])
//    }
//
//    private func configureAudioSession() {
//        do {
//            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: [.defaultToSpeaker])
//            try AVAudioSession.sharedInstance().setMode(AVAudioSession.Mode.videoChat)
//            try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
//            try AVAudioSession.sharedInstance().setActive(true)
//        } catch {}
//        rtcAudioSession.lockForConfiguration()
//        do {
//            try rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.defaultToSpeaker])
//            try rtcAudioSession.setMode(AVAudioSession.Mode.videoChat.rawValue)
//            try rtcAudioSession.overrideOutputAudioPort(.speaker)
//            try rtcAudioSession.setActive(true)
//        } catch let error {
//            delegate?.printDebugInfo("Error changing AVAudioSession category: \(error)")
//        }
//        rtcAudioSession.unlockForConfiguration()
//    }
}

extension WebRTCClient: RTCPeerConnectionDelegate {
    func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
        // delegate?.printDebugInfo("peerConnection new signaling state: \(stateChanged.rawValue)")
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
//        remoteStream = stream
//        if let track = stream.videoTracks.first {
//            track.add(remoteRenderView!)
//            if disableSoundOnStart {
//                switchMic(false)
//                switchSpeaker(false)
//            }
//        }
//        delegate?.printDebugInfo("peerConnection did add stream \(stream.debugDescription)")
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
//        delegate?.printDebugInfo("peerConnection did remote stream \(stream.debugDescription)")
    }

    func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
//        delegate?.printDebugInfo("peerConnection should negotiate")
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
//        delegate?.printDebugInfo("peerConnection new connection state: \(newState.rawValue)")
//        if newState == .connected {
//            configureAudioSession()
//            delegate?.webRTCClientStreamStarted(self)
//        }
//        delegate?.webRTCClient(self, didChangeConnectionState: newState)
//        print("--- DEBUG: connection changed \(newState.rawValue)")
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
//        delegate?.printDebugInfo("peerConnection new gathering state: \(newState.rawValue)")
//        delegate?.webRTCClient(self, didChangeGatheringState: newState)
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
        delegate?.didDiscoverLocalCandidate(candidate: candidate)
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
//        delegate?.printDebugInfo("peerConnection did remove candidate(s)")
    }

    func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
//        delegate?.printDebugInfo("peerConnection did open data channel")
//        remoteDataChannel = dataChannel
    }
}

// MARK: Audio control
// extension WebRTCClient {
//    func switchMic(_ isEnabled: Bool) {
//        delegate?.printDebugInfo("WebRTC client set mic enabled = \(isEnabled)")
//        peerConnection.transceivers
//            .compactMap { $0.sender.track as? RTCAudioTrack }
//            .forEach { $0.isEnabled = isEnabled }
//    }
//
//    func switchSpeaker(_ isEnabled: Bool) {
//        delegate?.printDebugInfo("WebRTC client speaker \(isEnabled)")
//        peerConnection.transceivers
//            .compactMap { $0.receiver.track as? RTCAudioTrack }
//            .forEach { $0.isEnabled = isEnabled }
//    }
// }
//
// extension WebRTCClient: RTCDataChannelDelegate {
//    func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
//        delegate?.printDebugInfo("dataChannel did change state: \(dataChannel.readyState)")
//    }
//
//    func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
//        delegate?.webRTCClient(self, didReceiveData: buffer.data)
//    }
// }
//
// extension WebRTCClient: RTCVideoViewDelegate {
//
//    func videoView(_ videoView: RTCVideoRenderer, didChangeVideoSize size: CGSize) {
//        delegate?.webRTCClient(self, didChangeVideoSize: size)
//    }
//
// }
