//
//  HumandCallKitPushNotificationAdapter.swift
//  humand
//

import Foundation
import PushKit
import StreamVideo

/// Custom adapter that decodes Humand's VoIP push payload format into Stream's Content struct.
///
/// Humand payload (flat):
/// ```
/// {
///   "id": "humand-call-id",
///   "name": "Caller Name",
///   "type": "CALL_INITIALIZED_V2",
///   "isGroup": false,
///   "providerCallId": "stream-call-id",
///   "initializationConfig": "{\"cameraEnabled\": false}"
/// }
/// ```
///
/// Stream expects (nested):
/// ```
/// { "stream": { "call_cid": "default:stream-call-id", ... } }
/// ```
class HumandCallKitPushNotificationAdapter: CallKitPushNotificationAdapter {

  /// Maps Stream call ID → push payload data for RN event emission and backend notifications.
  private var callDataMapping: [String: [String: Any]] = [:]

  override func decodePayload(_ payload: PKPushPayload) -> Content {
    // Ensure CallKit adapter is bound to CallManager's client before processing.
    // A livestream may have created a separate StreamVideo instance that overrode
    // the SDK's injected values.
    CallManager.shared.restoreCallKitBinding()

    let dict = payload.dictionaryPayload

    // providerCallId is the Stream call ID, id is the Humand call ID
    let streamCallId = dict[CallConstants.CallKeys.providerCallId] as? String ?? "unknown"
    let humandCallId = dict[CallConstants.CallKeys.id] as? String ?? "unknown"
    let cid = "default:\(streamCallId)"

    // Store full payload so we can emit to RN on accept and notify backend
    callDataMapping[streamCallId] = [
      CallConstants.CallKeys.id: humandCallId,
      CallConstants.CallKeys.name: dict[CallConstants.CallKeys.name] as? String ?? "",
      CallConstants.CallKeys.type: dict[CallConstants.CallKeys.type] as? String ?? CallConstants.NotificationTypes.callInitialized,
      CallConstants.CallKeys.providerCallId: streamCallId,
      CallConstants.CallKeys.isGroup: (dict[CallConstants.CallKeys.isGroup] as? Bool)
        ?? ((dict[CallConstants.CallKeys.isGroup] as? String)?.lowercased() == "true")
        ,
      CallConstants.CallKeys.initializationConfig: dict[CallConstants.CallKeys.initializationConfig] as? String ?? "{}"
    ]

    let callerName = dict[CallConstants.CallKeys.name] as? String ?? defaultCallText

    var hasVideo = false
    if let configString = dict[CallConstants.CallKeys.initializationConfig] as? String,
       let configData = configString.data(using: .utf8),
       let config = try? JSONSerialization.jsonObject(with: configData) as? [String: Any] {
      hasVideo = config["cameraEnabled"] as? Bool ?? false
    }

    let isGroup = callDataMapping[streamCallId]?[CallConstants.CallKeys.isGroup] as? Bool ?? false

    NativeCallsEventObserver.shared.observeCallRinging(
      callId: humandCallId,
      providerCallId: streamCallId,
      cameraEnabled: hasVideo,
      isGroup: isGroup,
      direction: "incoming"
    )

    // Update adapter call settings based on whether this is a video call
    let adapter = InjectedValues[\.callKitAdapter]
    adapter.callSettings = CallSettings(audioOn: true, videoOn: hasVideo, speakerOn: hasVideo)

    return Content(
      cid: cid,
      localizedCallerName: callerName,
      callerId: streamCallId,
      hasVideo: hasVideo
    )
  }

  /// Look up the stored call data for a given Stream call ID.
  func callData(forStreamCallId streamCallId: String) -> [String: Any]? {
    return callDataMapping[streamCallId]
  }

  /// Look up the Humand call ID for a given Stream call ID.
  func humandCallId(forStreamCallId streamCallId: String) -> String? {
    return callDataMapping[streamCallId]?[CallConstants.CallKeys.id] as? String
  }

  /// Remove mapping when call is finished.
  func removeMapping(forStreamCallId streamCallId: String) {
    callDataMapping.removeValue(forKey: streamCallId)
  }
}
