import Foundation
import CallKit
import UIKit

@objcMembers public class Settings: NSObject {
    private static let settingsKey = "CallingxSettings"

    // In-memory cache of the settings dictionary
    private static var cachedSettings: [String: Any]?
    private static let cacheQueue = DispatchQueue(label: "io.getstream.callingx.settings")

    public static func getSettings() -> [String: Any] {
        return cacheQueue.sync {
            if let cached = cachedSettings {
                return cached
            }
            cachedSettings = UserDefaults.standard.dictionary(forKey: settingsKey) ?? [:]
            return cachedSettings!
        }
    }

    public static func setSettings(_ options: [String: Any]?) {
        CallingxLog.settings.debugPublic("[setSettings] options = \(String(describing: options))")

        cacheQueue.sync {
            // Load lazily here rather than via getSettings() to avoid re-entrant sync on cacheQueue.
            var settings = cachedSettings ?? UserDefaults.standard.dictionary(forKey: settingsKey) ?? [:]

            if let options = options {
                for (key, value) in options {
                    settings[key] = value
                }
            }

            cachedSettings = settings
            UserDefaults.standard.set(settings, forKey: settingsKey)
        }
    }

    public static func getShouldRejectCallWhenBusy() -> Bool {
        guard let shouldReject = getSettings()["shouldRejectCallWhenBusy"] as? Bool else {
            return false
        }
        return shouldReject
    }

    public static func setShouldRejectCallWhenBusy(_ shouldReject: Bool) {
        setSettings(["shouldRejectCallWhenBusy": shouldReject])
    }

    public static func getSkipIncomingPushInForeground() -> Bool {
        guard let skip = getSettings()["skipIncomingPushInForeground"] as? Bool else {
            return false
        }
        return skip
    }

    public static func getProviderConfiguration() -> CXProviderConfiguration {
        CallingxLog.settings.debugPublic("[getProviderConfiguration]")
      
        let settings = getSettings()
        let providerConfiguration = CXProviderConfiguration()
        providerConfiguration.supportsVideo = true
        providerConfiguration.maximumCallGroups = 1
        providerConfiguration.maximumCallsPerCallGroup = 1
        providerConfiguration.supportedHandleTypes = getSupportedHandleTypes(settings["handleType"])

        if let supportsVideo = settings["supportsVideo"] as? Bool {
            providerConfiguration.supportsVideo = supportsVideo
        }
        if let maximumCallGroups = settings["maximumCallGroups"] as? Int {
            providerConfiguration.maximumCallGroups = maximumCallGroups
        }
        if let maximumCallsPerCallGroup = settings["maximumCallsPerCallGroup"] as? Int {
            providerConfiguration.maximumCallsPerCallGroup = maximumCallsPerCallGroup
        }

        if let imageName = settings["imageName"] as? String, !imageName.isEmpty {
            if let image = UIImage(named: imageName) {
                providerConfiguration.iconTemplateImageData = image.pngData()
            }
        }

        if let ringtoneSound = settings["ringtoneSound"] as? String, !ringtoneSound.isEmpty {
            providerConfiguration.ringtoneSound = ringtoneSound
        }

        if let includesCallsInRecents = settings["includesCallsInRecents"] as? Bool {
            providerConfiguration.includesCallsInRecents = includesCallsInRecents
        }

        return providerConfiguration
    }

  public static func getSupportedHandleTypes(_ handleType: Any?) -> Set<CXHandle.HandleType> {
        if let handleTypeArray = handleType as? [String] {
            var types = Set<CXHandle.HandleType>()
            for type in handleTypeArray {
                types.insert(getHandleType(type))
            }
            return types
        } else if let handleTypeString = handleType as? String {
            let type = getHandleType(handleTypeString)
            return Set([type])
        } else {
            return Set([CXHandle.HandleType.phoneNumber])
        }
    }

    public static func getHandleType(_ handleType: String?) -> CXHandle.HandleType {
        guard let handleType = handleType else { return .generic }

        switch handleType {
        case "generic":
            return .generic
        case "number", "phone":
            return .phoneNumber
        case "email":
            return .emailAddress
        default:
            return .generic
        }
    }
}
