import Foundation
import UserNotifications
import PushKit
import CallKit

@objc(AeThexConnectModule)
class AeThexConnectModule: NSObject {
  
  private var callKitProvider: CXProvider?
  private var callKitController: CXCallController?
  private var voipRegistry: PKPushRegistry?
  
  @objc
  static func requiresMainQueueSetup() -> Bool {
    return true
  }
  
  // MARK: - VoIP Push Notifications
  
  @objc
  func registerForVoIPPushes(_ resolver: @escaping RCTPromiseResolveBlock,
                              rejecter: @escaping RCTPromiseRejectBlock) {
    voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
    voipRegistry?.delegate = self
    voipRegistry?.desiredPushTypes = [.voIP]
    
    resolver(true)
  }
  
  // MARK: - CallKit Integration
  
  @objc
  func initializeCallKit() {
    let configuration = CXProviderConfiguration(localizedName: "AeThex Connect")
    configuration.supportsVideo = true
    configuration.maximumCallGroups = 1
    configuration.maximumCallsPerCallGroup = 1
    configuration.supportedHandleTypes = [.generic]
    configuration.iconTemplateImageData = UIImage(named: "CallKitIcon")?.pngData()
    
    callKitProvider = CXProvider(configuration: configuration)
    callKitProvider?.setDelegate(self, queue: nil)
    
    callKitController = CXCallController()
  }
  
  @objc
  func reportIncomingCall(_ callId: String,
                          callerName: String,
                          hasVideo: Bool,
                          resolver: @escaping RCTPromiseResolveBlock,
                          rejecter: @escaping RCTPromiseRejectBlock) {
    
    guard let provider = callKitProvider else {
      rejecter("NO_PROVIDER", "CallKit provider not initialized", nil)
      return
    }
    
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(type: .generic, value: callerName)
    update.hasVideo = hasVideo
    update.localizedCallerName = callerName
    
    let uuid = UUID(uuidString: callId) ?? UUID()
    
    provider.reportNewIncomingCall(with: uuid, update: update) { error in
      if let error = error {
        rejecter("CALL_ERROR", error.localizedDescription, error)
      } else {
        resolver(true)
      }
    }
  }
  
  @objc
  func startCall(_ callId: String,
                 recipientName: String,
                 hasVideo: Bool,
                 resolver: @escaping RCTPromiseResolveBlock,
                 rejecter: @escaping RCTPromiseRejectBlock) {
    
    guard let controller = callKitController else {
      rejecter("NO_CONTROLLER", "CallKit controller not initialized", nil)
      return
    }
    
    let uuid = UUID(uuidString: callId) ?? UUID()
    let handle = CXHandle(type: .generic, value: recipientName)
    let startCallAction = CXStartCallAction(call: uuid, handle: handle)
    startCallAction.isVideo = hasVideo
    
    let transaction = CXTransaction(action: startCallAction)
    
    controller.request(transaction) { error in
      if let error = error {
        rejecter("CALL_ERROR", error.localizedDescription, error)
      } else {
        resolver(true)
      }
    }
  }
  
  @objc
  func endCall(_ callId: String,
               resolver: @escaping RCTPromiseResolveBlock,
               rejecter: @escaping RCTPromiseRejectBlock) {
    
    guard let controller = callKitController else {
      rejecter("NO_CONTROLLER", "CallKit controller not initialized", nil)
      return
    }
    
    let uuid = UUID(uuidString: callId) ?? UUID()
    let endCallAction = CXEndCallAction(call: uuid)
    let transaction = CXTransaction(action: endCallAction)
    
    controller.request(transaction) { error in
      if let error = error {
        rejecter("CALL_ERROR", error.localizedDescription, error)
      } else {
        resolver(true)
      }
    }
  }
  
  // MARK: - Background Voice Chat
  
  @objc
  func startBackgroundVoice(_ resolver: @escaping RCTPromiseResolveBlock,
                             rejecter: @escaping RCTPromiseRejectBlock) {
    do {
      let audioSession = AVAudioSession.sharedInstance()
      try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .defaultToSpeaker])
      try audioSession.setActive(true)
      resolver(true)
    } catch {
      rejecter("AUDIO_ERROR", error.localizedDescription, error)
    }
  }
  
  @objc
  func stopBackgroundVoice(_ resolver: @escaping RCTPromiseResolveBlock,
                            rejecter: @escaping RCTPromiseRejectBlock) {
    do {
      let audioSession = AVAudioSession.sharedInstance()
      try audioSession.setActive(false)
      resolver(true)
    } catch {
      rejecter("AUDIO_ERROR", error.localizedDescription, error)
    }
  }
}

// MARK: - PKPushRegistryDelegate

extension AeThexConnectModule: PKPushRegistryDelegate {
  
  func pushRegistry(_ registry: PKPushRegistry,
                    didUpdate pushCredentials: PKPushCredentials,
                    for type: PKPushType) {
    let token = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
    
    // Send token to React Native
    NotificationCenter.default.post(
      name: NSNotification.Name("VoIPTokenReceived"),
      object: nil,
      userInfo: ["token": token]
    )
  }
  
  func pushRegistry(_ registry: PKPushRegistry,
                    didReceiveIncomingPushWith payload: PKPushPayload,
                    for type: PKPushType,
                    completion: @escaping () -> Void) {
    
    guard type == .voIP else {
      completion()
      return
    }
    
    // Extract call info from payload
    let callId = payload.dictionaryPayload["callId"] as? String ?? UUID().uuidString
    let callerName = payload.dictionaryPayload["callerName"] as? String ?? "Unknown"
    let hasVideo = payload.dictionaryPayload["hasVideo"] as? Bool ?? false
    
    // Report to CallKit
    reportIncomingCall(callId,
                      callerName: callerName,
                      hasVideo: hasVideo,
                      resolver: { _ in completion() },
                      rejecter: { _, _, _ in completion() })
  }
  
  func pushRegistry(_ registry: PKPushRegistry,
                    didInvalidatePushTokenFor type: PKPushType) {
    // Token invalidated
    print("VoIP token invalidated")
  }
}

// MARK: - CXProviderDelegate

extension AeThexConnectModule: CXProviderDelegate {
  
  func providerDidReset(_ provider: CXProvider) {
    // End all calls
    NotificationCenter.default.post(
      name: NSNotification.Name("CallsReset"),
      object: nil
    )
  }
  
  func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    // Answer call
    NotificationCenter.default.post(
      name: NSNotification.Name("AnswerCall"),
      object: nil,
      userInfo: ["callId": action.callUUID.uuidString]
    )
    
    action.fulfill()
  }
  
  func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
    // End call
    NotificationCenter.default.post(
      name: NSNotification.Name("EndCall"),
      object: nil,
      userInfo: ["callId": action.callUUID.uuidString]
    )
    
    action.fulfill()
  }
  
  func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
    // Mute/unmute
    NotificationCenter.default.post(
      name: NSNotification.Name("SetMuted"),
      object: nil,
      userInfo: [
        "callId": action.callUUID.uuidString,
        "muted": action.isMuted
      ]
    )
    
    action.fulfill()
  }
  
  func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
    // Hold/unhold
    NotificationCenter.default.post(
      name: NSNotification.Name("SetHeld"),
      object: nil,
      userInfo: [
        "callId": action.callUUID.uuidString,
        "held": action.isOnHold
      ]
    )
    
    action.fulfill()
  }
  
  func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    // Audio session activated
    NotificationCenter.default.post(
      name: NSNotification.Name("AudioSessionActivated"),
      object: nil
    )
  }
  
  func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
    // Audio session deactivated
    NotificationCenter.default.post(
      name: NSNotification.Name("AudioSessionDeactivated"),
      object: nil
    )
  }
}

// MARK: - React Native Bridge

@objc(AeThexConnectModuleBridge)
class AeThexConnectModuleBridge: NSObject {
  
  @objc
  static func requiresMainQueueSetup() -> Bool {
    return true
  }
}
