//
//  CallController.swift
//  SimpleX (iOS)
//
//  Created by Evgeny on 21/05/2022.
//  Copyright © 2022 SimpleX Chat. All rights reserved.
//

import Foundation
//import CallKit
import AVFoundation
import SimpleXChat

//class CallController: NSObject, CXProviderDelegate, ObservableObject {
class CallController: NSObject, ObservableObject {
    static let useCallKit = false
    static let shared = CallController()
//    private let provider = CXProvider(configuration: CallController.configuration)
//    private let controller = CXCallController()
    private let callManager = CallManager()
    @Published var activeCallInvitation: RcvCallInvitation?

// PKPushRegistry will be used from notification service extension
//    let registry = PKPushRegistry(queue: nil)

//    static let configuration: CXProviderConfiguration = {
//        let configuration = CXProviderConfiguration()
//        configuration.supportsVideo = true
//        configuration.supportedHandleTypes = [.generic]
//        configuration.includesCallsInRecents = true // TODO disable or add option
//        configuration.maximumCallsPerCallGroup = 1
//        return configuration
//    }()

    override init() {
        super.init()
//        self.provider.setDelegate(self, queue: nil)
//        self.registry.delegate = self
//        self.registry.desiredPushTypes = [.voIP]
    }

//    func providerDidReset(_ provider: CXProvider) {
//    }

//    func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
//        logger.debug("CallController.provider CXStartCallAction")
//        if callManager.startOutgoingCall(callUUID: action.callUUID) {
//            action.fulfill()
//            provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
//        } else {
//            action.fail()
//        }
//    }

//    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
//        logger.debug("CallController.provider CXAnswerCallAction")
//        if callManager.answerIncomingCall(callUUID: action.callUUID) {
//            action.fulfill()
//        } else {
//            action.fail()
//        }
//    }

//    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
//        logger.debug("CallController.provider CXEndCallAction")
//        callManager.endCall(callUUID: action.callUUID) { ok in
//            if ok {
//                action.fulfill()
//            } else {
//                action.fail()
//            }
//        }
//    }

//    func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
//        print("timed out", #function)
//        action.fulfill()
//    }

//    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
//        print("received", #function)
////        do {
////            try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers)
////            logger.debug("audioSession category set")
////            try audioSession.setActive(true)
////            logger.debug("audioSession activated")
////        } catch {
////            print(error)
////            logger.error("failed activating audio session")
////        }
//    }

//    func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
//        print("received", #function)
//    }

//    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
//
//    }

// This will be needed when we have notification service extension
//    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
//        if type == .voIP {
//           // Extract the call information from the push notification payload
//            if let displayName = payload.dictionaryPayload["displayName"] as? String,
//               let contactId = payload.dictionaryPayload["contactId"] as? String,
//               let uuidStr = payload.dictionaryPayload["uuid"] as? String,
//               let uuid = UUID(uuidString: uuidStr) {
//                let callUpdate = CXCallUpdate()
//                callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: displayName)
//                provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
//                    if error != nil {
//                        let m = ChatModel.shared
//                        m.callInvitations.removeValue(forKey: contactId)
//                    }
//                    // Tell PushKit that the notification is handled.
//                    completion()
//                })
//            }
//        }
//    }

    func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
        logger.debug("CallController.reportNewIncomingCall")
//        if CallController.useCallKit, let uuid = invitation.callkitUUID {
//            let update = CXCallUpdate()
//            update.remoteHandle = CXHandle(type: .generic, value: invitation.contact.displayName)
//            update.hasVideo = invitation.peerMedia == .video
//            provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
//        } else {
            NtfManager.shared.notifyCallInvitation(invitation)
            if invitation.callTs.timeIntervalSinceNow >= -180 {
                activeCallInvitation = invitation
            }
//        }
    }

//    func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
//        if CallController.useCallKit, let uuid = call.callkitUUID {
//            provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
//        }
//    }

    func reportCallRemoteEnded(invitation: RcvCallInvitation) {
//        if CallController.useCallKit, let uuid = invitation.callkitUUID {
//            provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
//        } else if invitation.contact.id == activeCallInvitation?.contact.id {
            activeCallInvitation = nil
//        }
    }

//    func reportCallRemoteEnded(call: Call) {
//        if CallController.useCallKit, let uuid = call.callkitUUID {
//            provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
//        }
//    }

    func startCall(_ contact: Contact, _ media: CallMediaType) {
        logger.debug("CallController.startCall")
        let uuid = callManager.newOutgoingCall(contact, media)
//        if CallController.useCallKit {
//            let handle = CXHandle(type: .generic, value: contact.displayName)
//            let action = CXStartCallAction(call: uuid, handle: handle)
//            action.isVideo = media == .video
//            requestTransaction(with: action)
//        } else if callManager.startOutgoingCall(callUUID: uuid) {
        if callManager.startOutgoingCall(callUUID: uuid) {
            logger.debug("CallController.startCall: call started")
        } else {
            logger.error("CallController.startCall: no active call")
        }
    }

    func answerCall(invitation: RcvCallInvitation) {
        callManager.answerIncomingCall(invitation: invitation)
        if invitation.contact.id == self.activeCallInvitation?.contact.id {
            self.activeCallInvitation = nil
        }
    }

    func endCall(callUUID: UUID) {
//        if CallController.useCallKit {
//            requestTransaction(with: CXEndCallAction(call: callUUID))
//        } else {
            callManager.endCall(callUUID: callUUID) { ok in
                if ok {
                    logger.debug("CallController.endCall: call ended")
                } else {
                    logger.error("CallController.endCall: no actove call pr call invitation to end")
                }
            }
//        }
    }

    func endCall(invitation: RcvCallInvitation) {
        callManager.endCall(invitation: invitation) {
            if invitation.contact.id == self.activeCallInvitation?.contact.id {
                DispatchQueue.main.async {
                    self.activeCallInvitation = nil
                }
            }
        }
    }

    func endCall(call: Call, completed: @escaping () -> Void) {
        callManager.endCall(call: call, completed: completed)
    }

    func callAction(invitation: RcvCallInvitation, action: NtfCallAction) {
        switch action {
        case .accept: answerCall(invitation: invitation)
        case .reject: endCall(invitation: invitation)
        }
    }

//    private func requestTransaction(with action: CXAction) {
//        let t = CXTransaction()
//        t.addAction(action)
//        controller.request(t) { error in
//            if let error = error {
//                logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
//            } else {
//                logger.debug("CallController.requestTransaction requested transaction successfully")
//            }
//        }
//    }
}
