import PRESTOplay

public class BridgeDeserializer {
    public static func toPlayerConfiguration(
        _ jsonPlayerConfiguration: [String: Any]
    )
        throws -> PlayerConfiguration
    {
        guard let jsonSource = jsonPlayerConfiguration["source"] as? [String: Any] else {
            throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Source not found")
        }

        guard
            let jsonSourceUrl = jsonSource["url"] as? String,
            let url = URL(string: jsonSourceUrl)
        else {
            throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Source URL not found or invalid")
        }

        guard let jsonSourceContentType = jsonSource["type"] as? String else {
            throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Source content type not found")
        }

        let playerConfiguration = PlayerConfiguration(with: url)

        let contentType = try toContentType(jsonSourceContentType)
        playerConfiguration.contentType = contentType

        playerConfiguration.userId = jsonPlayerConfiguration["userId"] as? String
        if let startTimeMs = jsonPlayerConfiguration["startTimeMs"] as? Double {
            playerConfiguration.startTime = startTimeMs / 1000.0
        } else {
            playerConfiguration.startTime = 0
        }
        playerConfiguration.autoPlay = jsonPlayerConfiguration["autoPlay"] as? Bool
        playerConfiguration.preferredTextLanguage = jsonPlayerConfiguration["preferredTextLanguage"] as? String
        playerConfiguration.preferredAudioLanguage = jsonPlayerConfiguration["preferredAudioLanguage"] as? String
        playerConfiguration.sideloadedTracks = try toSideloadedTracks(jsonPlayerConfiguration)
        playerConfiguration.networkConfiguration = toNetworkConfiguration(jsonPlayerConfiguration)
        playerConfiguration.liveConfiguration = toLiveConfiguration(jsonPlayerConfiguration)
                
        playerConfiguration.metaData = MetaData()
        
        if let metadata = jsonPlayerConfiguration["metaData"] as? [String: Any] {
            if let title = metadata["title"] as? String {
                playerConfiguration.metaData?.title = title
            }
            if let subtitle = metadata["subtitle"] as? String {
                playerConfiguration.metaData?.byline = subtitle
            }
            if let artworkUrl = metadata["artworkUrl"] as? String {
                playerConfiguration.metaData?.posterUrl = artworkUrl
            }
            if let channelDescription = metadata["channelDescription"] as? String {
                playerConfiguration.metaData?.metadataDescription = channelDescription
            }
                
        }

        if let drmConfig = jsonPlayerConfiguration["drm"] as? [String: Any], !drmConfig.isEmpty {
            playerConfiguration.drmConfiguration = DrmConfiguration()
            /**
             * By default, AirPlay is disabled for DRM-encrypted content.
             * To enable, set preventSecondScreenPlayback to false in the DRM configuration.
             */
            var preventSecondScreenPlayback = true;
            if let secondaryDisplayBehaviour = jsonPlayerConfiguration["secondaryDisplayBehaviour"] as? String {
                if "never" == secondaryDisplayBehaviour {
                    preventSecondScreenPlayback = true;
                }
                if "allowAlways" == secondaryDisplayBehaviour {
                    preventSecondScreenPlayback = false;
                }
            }
            
            playerConfiguration.drmConfiguration?.preventSecondScreenPlayback = preventSecondScreenPlayback;

            if (contentType == .smooth || contentType == .dash)
            {
                playerConfiguration.drmType = .custom
                playerConfiguration.drmSystem = .widevine
                // NOTE: This is a placeholder, ReactNative SDK MUST set it in request modifier
                playerConfiguration.drmConfiguration?.licensingUrl = URL(string:Constants.licenseUrlPlaceholder)
            }
            else if (contentType == .hls || contentType == .mp4)
            {
                playerConfiguration.drmType = .custom
                playerConfiguration.drmSystem = .fairplay
                playerConfiguration.drmConfiguration?.preventSecondScreenPlayback = preventSecondScreenPlayback;
                // NOTE: This is a placeholder, ReactNative SDK MUST set it in request modifier
                playerConfiguration.drmConfiguration?.licensingUrl = URL(string: Constants.licenseUrlPlaceholder)
                playerConfiguration.drmConfiguration?.certificateUrl = URL(string: Constants.certificateUrlPlaceholder)
            }
        }
        
        /**
         * Offline playing
         * https://castlabs.atlassian.net/browse/RN-418
         */
        if let originalUrl = jsonSource["originalUrl"] as? String {
            playerConfiguration.originUrl = URL(string: originalUrl);
        }

        return playerConfiguration
    }
    
    public static func toSilentSwitchBehaviour(_ jsonPlayerConfiguration: [String: Any]) throws -> SilentSwitchBehaviour {
        if
            let jsonAppleConfiguration = jsonPlayerConfiguration["apple"] as? [String: Any],
            let jsonSilentSwitchBehaviour = jsonAppleConfiguration["silentSwitchBehaviour"] as? String
        {
            switch(jsonSilentSwitchBehaviour) {
            case "ignore":
                return .ignore
            case "obey":
                return .obey
            default:
                throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Unknown silent switch behaviour")
            }
        }

        return .auto
    }

    public static func toAppliesMediaSelectionCriteriaAutomatically(_ jsonPlayerConfiguration: [String: Any]) -> Bool? {
        if
            let jsonAppleConfiguration = jsonPlayerConfiguration["apple"] as? [String: Any],
            let appliesMediaSelectionCriteriaAutomatically = jsonAppleConfiguration["appliesMediaSelectionCriteriaAutomatically"] as? Bool
        {
            return appliesMediaSelectionCriteriaAutomatically
        }

        return nil
    }

    public static func toNetworkConfiguration(_ jsonPlayerConfiguration: [String: Any]) -> NetworkConfiguration? {
        if
            let jsonAppleConfiguration = jsonPlayerConfiguration["apple"] as? [String: Any],
            let jsonAppleNetworkConfiguration = jsonAppleConfiguration["networkConfiguration"] as? [String: Any]
        {
            let networkConfiguration: NetworkConfiguration = NetworkConfiguration()

            // We're explicitly copying the properties
            // to make the mapping clearer for the support team.

            if let val = jsonAppleNetworkConfiguration["httpAdditionalHeaders"] as? [String : String] {
                networkConfiguration.httpAdditionalHeaders = val
            }
            if let val = jsonAppleNetworkConfiguration["allowsCellularAccess"] as? Bool {
                networkConfiguration.allowsCellularAccess = val
            }
            if let val = jsonAppleNetworkConfiguration["timeoutIntervalForResourceMs"] as? Double {
                networkConfiguration.timeoutIntervalForResourceMs = val
            }
            if let val = jsonAppleNetworkConfiguration["timeoutIntervalForDrmAcquisitionMs"] as? Double {
                networkConfiguration.timeoutIntervalForDrmAcquisitionMs = val
            }
            if let val = jsonAppleNetworkConfiguration["waitsForConnectivity"] as? Bool {
                networkConfiguration.waitsForConnectivity = val
            }

            return networkConfiguration
        }

        return nil
    }

    public static func toContentType(_ jsonSourceContentType: String) throws -> ContentType {
        switch jsonSourceContentType {
        case "smooth":
            return .smooth
        case "dash":
            return .dash
        case "hls":
            return .hls
        case "mp4":
            return .mp4
        default:
            throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Unknown source content type")
        }
    }

    public static func toSideloadedTracks(
        _ jsonPlayerConfiguration: [String: Any]
    )
        throws -> [SideloadedTrack]
    {
        var sideloadedTracks: [SideloadedTrack] = []

        // Parse remote thumbnail tracks

        if
            let jsonRemoteThumbnailTracks =
            jsonPlayerConfiguration["remoteThumbnailTracks"] as? [[String: Any]]
        {
            for jsonRemoteThumbnailTrack in jsonRemoteThumbnailTracks {
                guard
                    let jsonUrl = jsonRemoteThumbnailTrack["url"] as? String,
                    let url = URL(string: jsonUrl) else
                {
                    throw PrestoPlayError(
                        .fatal,
                        .invalidConfiguration,
                        message: "Remote thunmbnail track URL not defined or invalid")
                }

                var sideloadedTrack = SideloadedTrack(with: url, trackType: .thumbnail)

                sideloadedTrack.mimeType = jsonRemoteThumbnailTrack["mimeType"] as? String
                sideloadedTrack.gridWidth = jsonRemoteThumbnailTrack["gridWidth"] as? Int
                sideloadedTrack.gridHeight = jsonRemoteThumbnailTrack["gridHeight"] as? Int
                sideloadedTrack.intervalMs = jsonRemoteThumbnailTrack["intervalMs"] as? Int

                sideloadedTracks.append(sideloadedTrack)
            }
        }

        if
            let jsonRemoteTextTracks =
            jsonPlayerConfiguration["remoteTextTracks"] as? [[String: Any]]
        {
            for jsonRemoteTextTrack in jsonRemoteTextTracks {
                guard
                    let jsonUrl = jsonRemoteTextTrack["url"] as? String,
                    let url = URL(string: jsonUrl) else
                {
                    throw PrestoPlayError(.fatal, .invalidConfiguration, message: "Remote text track URL not defined or invalid")
                }

                var sideloadedTrack = SideloadedTrack(with: url, trackType: .text)

                sideloadedTrack.mimeType = jsonRemoteTextTrack["mimeType"] as? String
                sideloadedTrack.language = jsonRemoteTextTrack["language"] as? String
                sideloadedTrack.label = jsonRemoteTextTrack["label"] as? String
                sideloadedTrack.kind = "subtitle"

                sideloadedTracks.append(sideloadedTrack)
            }
        }

        return sideloadedTracks
    }
    
    public static func toLiveConfiguration(
        _ jsonPlayerConfiguration: [String: Any]
    ) -> LiveConfiguration? {
        guard
            let jsonLiveConfiguration = jsonPlayerConfiguration["liveConfiguration"] as? [String: Any],
            let liveEdgeLatencyMs = jsonLiveConfiguration["liveEdgeLatencyMs"] as? Int else {
                return nil
            }
        return LiveConfiguration(liveEdgeLatencyMs: liveEdgeLatencyMs)
    }

    public static func toData(
        base64Encoded: String
    )
        -> Data?
    {
        Data(base64Encoded: base64Encoded)
    }
    
    public static func getLogLevel(reactLogLevel:Int) -> CLLogLevel {
        switch reactLogLevel {
            case 0:
                return .none
            case 1, 2, 3:
                return .error
            case 4:
                fallthrough
            default:
                return .debug
            }
    }
}
