import PluggerCore

@objc(PluggerReactNative)
public class PluggerReactNative: NSObject {
  
  @objc
  public static let shared = PluggerReactNative()

  public static var sharedHttpConfig: HTTPConfig = HTTPConfig(apiBaseUrl: "")
  public static var pluginConfigsMap: [String: AnyObject] = [:]

  private func rejectHandler(reject: RCTPromiseRejectBlock, error: Error) -> Void {
    reject(error.localizedDescription, error.localizedDescription, nil)
  }

  public static func addPlugin(plugin: Plugin) -> Void {
    do {
      try Plugger.addPlugin(plugin)
    } catch {
      print(error.localizedDescription)
    }
  }

  public static func getPlugin<T>(pluginType: T.Type) throws -> T? where T : PluggerCore.PluginInterface {
    var plugin: T? = nil
    do {
      plugin = try Plugger.getPlugin(pluginType.self)
    } catch {
      throw error
    }
    return plugin
  }

  public static func getPluginConfig(pluginName: String) -> AnyObject {
    return PluggerReactNative.pluginConfigsMap[pluginName]!
  }

  @objc(initializePlugger:withResolver:withRejecter:)
  func initializePlugger(config: [String: AnyObject],resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
    do {
      let initConfig = makePluggerConfig(rnConfig: config)
      _ = try Plugger.initialize(with: initConfig)
      resolve(nil)
    } catch {
      rejectHandler(reject: reject, error: error)
    }
  }

  @objc(isPluggerInitialized:withRejecter:)
  func isPluggerInitialized(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    resolve(Plugger.isInitialized())
  }

  @objc(setUser:withResolver:withRejecter:)
  func setUser(userId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    Plugger.user.setUser(userId: userId)
    resolve(nil)
  }

  @objc(setGuest:withResolver:withRejecter:)
  func setGuest(guestId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    Plugger.user.setGuest(guestId: guestId)
    resolve(nil)
  }

  @objc(setLocation:withLongitude:withResolver:withRejecter:)
  func setLocation(latitude: Double, longitude: Double, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    Plugger.user.setLocation(latitude: latitude, longitude: longitude)
    resolve(nil)
  }

  static func buildClientConfig(config: [String: AnyObject]) -> ClientConfig {
    var clientConfig: ClientConfig = ClientConfig(apiKey: "")
    let rnApiKey = config["apiKey"] as? String
    clientConfig = ClientConfig(apiKey: rnApiKey ?? "")
    
    return clientConfig
  }

  public static func buildHttpConfig(rnHttpConfig: [String: AnyObject]) -> HTTPConfig {
    var apiBaseUrl = ""
    var shouldRetry = true
    var headers: [String: String] = [:]
    var attempts = 3
    var time = 500
    var policy = RetrialDelayPolicy.linear
    var callTimeout = 20000
    var omitRetrialForStatusCode: Set<Int> = []
    if let baseUrl = rnHttpConfig["apiBaseUrl"] as? String {
      apiBaseUrl = baseUrl
    }
    if let rnShouldRetry = rnHttpConfig["shouldRetry"] as? Bool {
      shouldRetry = rnShouldRetry
    }
    if let rnHeaders = rnHttpConfig["headers"] as? [String: String] {
      headers = rnHeaders
    }
    if let rnRetrialConfig = rnHttpConfig["retrialConfig"] as? [String: AnyObject] {
      if let rnAttempts = rnRetrialConfig["attempts"] as? Int {
        attempts = rnAttempts
      }
      if let rnOmitRetrialForStatusCode = rnRetrialConfig["omittedRetrialCodes"] as? [Int] {
        omitRetrialForStatusCode = Set<Int>(rnOmitRetrialForStatusCode)
      }
      if let rnDelayConfig = rnRetrialConfig["delay"] as? [String: AnyObject] {
        if let rnTime = rnDelayConfig["time"] as? Int {
          time = rnTime
        }
        if let rnPolicy = rnDelayConfig["policy"] as? String {
          if rnPolicy == "LINEAR" {
            policy = RetrialDelayPolicy.linear
          } else if rnPolicy == "INCREMENTAL" {
            policy = RetrialDelayPolicy.incremental
          } else if rnPolicy == "QUADRATIC" {
            policy = RetrialDelayPolicy.quadratic
          }
        }
      }
    }
    if let rnTimeoutDelay = rnHttpConfig["timeoutConfig"] as? [String: AnyObject] {
      if let rnCallTimeout = rnTimeoutDelay["callTimeout"] as? Int {
        callTimeout = rnCallTimeout
      }
    }

    let httpConfig = HTTPConfig(
      headers: headers,
      apiBaseUrl: apiBaseUrl,
      shouldRetry: shouldRetry,
      retrialConfig: RetrialConfig(
        attempts:attempts,
        delay: Delay(
          time: time,
          policy: policy
        ),
        omitRetrialForStatusCode: omitRetrialForStatusCode
      ),
      timeoutConfig: TimeoutConfig(
        callTimeout: callTimeout
      )
    )

    PluggerReactNative.sharedHttpConfig = httpConfig
    return httpConfig
  }

  func makePluggerConfig(rnConfig: [String: AnyObject]) -> CommonConfig {
    let plugins: [Plugin] = []
    var httpConfig: HTTPConfig = HTTPConfig(apiBaseUrl: "")
    var clientConfig: ClientConfig = ClientConfig(apiKey: "")

    if let rnHttpConfig = rnConfig["httpConfig"] as? [String: AnyObject] {
      httpConfig = PluggerReactNative.buildHttpConfig(rnHttpConfig: rnHttpConfig)
    }

    if let rnClientConfig = rnConfig["clientConfig"] as? [String: AnyObject] {
      clientConfig = PluggerReactNative.buildClientConfig(config: rnClientConfig)
    }

    if let rnPlugins = rnConfig["plugins"] as? [AnyObject] {
      for plugin in rnPlugins {
        var pluginName = ""
        var pluginConfig: AnyObject
        if let rnPluginName = plugin["name"] as? String {
          pluginName = rnPluginName
        }
        if let rnPluginConfig = plugin["config"] as? AnyObject {
          pluginConfig = rnPluginConfig
          PluggerReactNative.pluginConfigsMap[pluginName] = pluginConfig
        }
      }
    }

    return CommonConfig(plugins: plugins, httpConfig: httpConfig, clientConfig: clientConfig)
  }
    
    @objc
    public func initializePluggerSync(config: [String: AnyObject]) -> Bool {
      do {
        let initConfig = makePluggerConfig(rnConfig: config)
        _ = try Plugger.initialize(with: initConfig)
        return true
      } catch {
        print(error)
        return false
      }
    }
    
    @objc
    public func isPluggerInitializedSync() -> Bool {
      return Plugger.isInitialized()
    }

    @objc
    public func setUserSync(userId: String) -> Void {
      Plugger.user.setUser(userId: userId)
    }

    @objc
    public func setGuestSync(guestId: String) -> Void {
      Plugger.user.setGuest(guestId: guestId)
    }

    @objc
    public func setLocationSync(latitude: String, longitude: String) -> Void {
        Plugger.user.setLocation(latitude: Double(latitude) ?? 0.0, longitude: Double(longitude) ?? 0.0)
    }
}
