import AVFoundation
import Promises

class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate {
    
    private var _loadingRequests: [String: AVAssetResourceLoadingRequest?] = [:]
    private var _requestingCertificate:Bool = false
    private var _requestingCertificateErrored:Bool = false
    private var _drm: DRMParams?
    private var _localSourceEncryptionKeyScheme: String?
    private var _reactTag: NSNumber?
    private var _onVideoError: RCTDirectEventBlock?
    private var _onGetLicense: RCTDirectEventBlock?
    
    
    init(
        asset: AVURLAsset,
        drm: DRMParams?,
        localSourceEncryptionKeyScheme: String?,
        onVideoError: RCTDirectEventBlock?,
        onGetLicense: RCTDirectEventBlock?,
        reactTag: NSNumber
    ) {
        super.init()
        let queue = DispatchQueue(label: "assetQueue")
        asset.resourceLoader.setDelegate(self, queue: queue)
        _reactTag = reactTag
        _onVideoError = onVideoError
        _onGetLicense = onGetLicense
        _drm = drm
        _localSourceEncryptionKeyScheme = localSourceEncryptionKeyScheme
    }
    
    deinit {
        for request in _loadingRequests.values {
            request?.finishLoading()
        }
    }
    
    func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest:AVAssetResourceRenewalRequest) -> Bool {
        return loadingRequestHandling(renewalRequest)
    }
    
    func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest:AVAssetResourceLoadingRequest) -> Bool {
        return loadingRequestHandling(loadingRequest)
    }
    
    func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) {
        RCTLog("didCancelLoadingRequest")
    }

    func setLicenseResult(_ license:String!,_ licenseUrl: String!) {
        
        // Check if the loading request exists in _loadingRequests based on licenseUrl
        guard let loadingRequest = _loadingRequests[licenseUrl] else {
            setLicenseResultError("Loading request for licenseUrl \(licenseUrl) not found", licenseUrl)
            return
        }
        
        // Check if the license data is valid
        guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license) else {
            setLicenseResultError("No data from JS license response", licenseUrl)
            return
        }
        
        let dataRequest: AVAssetResourceLoadingDataRequest! = loadingRequest?.dataRequest
        dataRequest.respond(with: respondData)
        loadingRequest!.finishLoading()
        _loadingRequests.removeValue(forKey: licenseUrl)
    }
    
    func setLicenseResultError(_ error:String!,_ licenseUrl: String!) {
        // Check if the loading request exists in _loadingRequests based on licenseUrl
        guard let loadingRequest = _loadingRequests[licenseUrl] else {
            print("Loading request for licenseUrl \(licenseUrl) not found. Error: \(error)")
            return
        }

        self.finishLoadingWithError(error: RCTVideoErrorHandler.fromJSPart(error), licenseUrl: licenseUrl)
    }
    
    func finishLoadingWithError(error: Error!, licenseUrl: String!) -> Bool {
        // Check if the loading request exists in _loadingRequests based on licenseUrl
        guard let loadingRequest = _loadingRequests[licenseUrl], let error = error as NSError? else {
            // Handle the case where the loading request is not found or error is nil
            return false
        }

        loadingRequest!.finishLoading(with: error)
        _loadingRequests.removeValue(forKey: licenseUrl)
        _onVideoError?([
            "error": [
                "code": NSNumber(value: error.code),
                "localizedDescription": error.localizedDescription ?? "",
                "localizedFailureReason": error.localizedFailureReason ?? "",
                "localizedRecoverySuggestion": error.localizedRecoverySuggestion ?? "",
                "domain": error.domain
            ],
            "target": _reactTag
        ])

        return false
    }

    
    func loadingRequestHandling(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
        if handleEmbeddedKey(loadingRequest) {
            return true
        }
        
        if _drm != nil {
            return handleDrm(loadingRequest)
        }
        
       return false
    }
    
    func handleEmbeddedKey(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
        guard let url = loadingRequest.request.url,
              let _localSourceEncryptionKeyScheme = _localSourceEncryptionKeyScheme,
              let persistentKeyData = RCTVideoUtils.extractDataFromCustomSchemeUrl(from: url, scheme: _localSourceEncryptionKeyScheme)
        else {
            return false
        }
        
        loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType
        loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
        loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count)
        loadingRequest.dataRequest?.respond(with: persistentKeyData)
        loadingRequest.finishLoading()
        
        return true
    }
    
    func handleDrm(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
        if _requestingCertificate {
            return true
        } else if _requestingCertificateErrored {
            return false
        }

        var requestKey: String = loadingRequest.request.url?.absoluteString ?? ""

        _loadingRequests[requestKey] = loadingRequest
        
        guard let _drm = _drm, let drmType = _drm.type, drmType == "fairplay" else {
            return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData, licenseUrl: requestKey)
        }
        
        var promise: Promise<Data>
        if _onGetLicense != nil {
            let contentId = _drm.contentId ?? loadingRequest.request.url?.host
            promise = RCTVideoDRM.handleWithOnGetLicense(
                loadingRequest:loadingRequest,
                contentId:contentId,
                certificateUrl:_drm.certificateUrl,
                base64Certificate:_drm.base64Certificate
            ) .then{ spcData -> Void in
                self._requestingCertificate = true
                self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? loadingRequest.request.url?.absoluteString ?? "",
                                     "contentId": contentId ?? "",
                                     "spcBase64": spcData.base64EncodedString(options: []),
                                     "target": self._reactTag])
            }
        } else {
            promise = RCTVideoDRM.handleInternalGetLicense(
                loadingRequest:loadingRequest,
                contentId:_drm.contentId,
                licenseServer:_drm.licenseServer,
                certificateUrl:_drm.certificateUrl,
                base64Certificate:_drm.base64Certificate,
                headers:_drm.headers
            ) .then{ data -> Void in
                    guard let dataRequest = loadingRequest.dataRequest else {
                        throw RCTVideoErrorHandler.noCertificateData
                    }
                    dataRequest.respond(with:data)
                    loadingRequest.finishLoading()
                }
        }
        
        
        promise.catch{ error in
            self.finishLoadingWithError(error:error, licenseUrl: requestKey)
            self._requestingCertificateErrored = true
        }
        
        return true
    }
}