//
//  IncomingCallVC.swift
//  WoWonder
//
//  Created by iMac on 29/09/23.
//  Copyright © 2023 ScriptSun. All rights reserved.
//

import UIKit
import SDWebImage
import Async
import Toast_Swift
import WowonderMessengerSDK
import AVFAudio
import AVFoundation

enum CallType: String {
    case audio
    case video
}

enum CallProvider: String {
    case agora
    case twilio
}

class IncomingCallVC: BaseVC {
    
    // MARK: - IBOutlets
    
    @IBOutlet weak var userImageView: UIImageView!
    @IBOutlet weak var userNameLabel: UILabel!
    @IBOutlet weak var callTypeLabel: UILabel!
    
    // MARK: - Properties
    
    var callData: UserCallData?
    private var checkCallTimer = Timer()
    private var callDeclinedTimer = Timer()
    private var callDeclinedCount = 0
    private var callAudioPlayer: AVAudioPlayer?
    var callProvider: CallProvider = .agora
    var callType: CallType = .audio
    
    // MARK: - View Life Cycles
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        self.initialConfig()
    }
    
    // MARK: - Selectors
    
    // Message Button Action
    @IBAction func messageButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        if let newVC = R.storyboard.popup.callMessageAlertVC() {
            newVC.delegate = self
            newVC.modalPresentationStyle = .overCurrentContext
            newVC.modalTransitionStyle = .crossDissolve
            self.present(newVC, animated: true)
        }
    }
    
    // Answer Call Button Action
    @IBAction func answerCallButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        switch self.callProvider {
        case .agora:
            self.answerAgoraCall(call_id: self.callData?.data?.id ?? "")
        case .twilio:
            switch self.callType {
            case .audio:
                self.answerTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "audio")
            case .video:
                self.answerTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "video")
            }
        }
    }
    
    // Decline Call Button Action
    @IBAction func declineCallButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        switch self.callProvider {
        case .agora:
            self.declineAgoraCall(call_id: self.callData?.data?.id ?? "")
        case .twilio:
            switch self.callType {
            case .audio:
                self.declineTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "audio")
            case .video:
                self.declineTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "video")
            }
        }
    }
    
    // MARK: - Helper Functions
    
    // Initial Config
    func initialConfig() {
        self.setData()
        self.playCallSound()
        self.checkCallTimer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(self.checkForCallStatus), userInfo: nil, repeats: true)
    }
    
    @objc func checkForCallStatus() {
        switch self.callProvider {
        case .agora:
            self.checkForAgoraCallAction(call_id: self.callData?.data?.id ?? "")
        case .twilio:
            switch self.callType {
            case .audio:
                self.checkForTwilioCallAction(call_id: self.callData?.data?.id ?? "", call_type: "audio")
            case .video:
                self.checkForTwilioCallAction(call_id: self.callData?.data?.id ?? "", call_type: "video")
            }
        }
    }
    
    // Set Data
    func setData() {
        let url = URL(string: self.callData?.avatar ?? "")
        let indicator = SDWebImageActivityIndicator.medium
        self.userImageView.sd_imageIndicator = indicator
        DispatchQueue.global(qos: .userInteractive).async {
            self.userImageView.sd_setImage(with: url, placeholderImage: UIImage(named: "dm_image"))
        }
        self.userNameLabel.text = self.callData?.name ?? ""
        switch self.callType {
        case .audio:
            self.callTypeLabel.text = "Voice call"
        case .video:
            self.callTypeLabel.text = "Video call"
        }
    }
    
    private func closeScreen(_ completion: (() -> Void)? = nil) {
        self.checkCallTimer.invalidate()
        self.callDeclinedTimer.invalidate()
        self.stopCallSound()
        self.navigationController?.popToRootViewController(animated: true)
    }
    
    @objc private func incrementCallDeclinedCount() {
        self.callDeclinedCount += 1
    }
    
    private func playCallSound() {
        guard let url = Bundle.main.url(forResource: "mystic_call", withExtension: "mp3") else { return }
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            try AVAudioSession.sharedInstance().setActive(true)
            self.callAudioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
            guard let aPlayer = self.callAudioPlayer else { return }
            aPlayer.play()
        } catch let error {
            print(error.localizedDescription)
        }
    }
    
    private func stopCallSound() {
        self.callAudioPlayer?.stop()
    }
    
}

// MARK: - Extensions

// MARK: Api Call
extension IncomingCallVC {
    
    func sendMessage(text: String) {
        let sessionToken = AppInstance.instance._sessionId
        ChatManager.instance.sendMessage(message: text, session_token: sessionToken, ReceipientId: self.callData?.user_id ?? "", lat: 0, lng: 0, reply_id: "", story_id: "") { Success, AuthError, ServerKeyError, error in
            if Success != nil {
                Async.main {
                    self.dismissProgressDialog {
                        switch self.callProvider {
                        case .agora:
                            self.declineAgoraCall(call_id: self.callData?.data?.id ?? "")
                        case .twilio:
                            switch self.callType {
                            case .audio:
                                self.declineTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "audio")
                            case .video:
                                self.declineTwilioCall(call_id: self.callData?.data?.id ?? "", call_type: "video")
                            }
                        }
                    }
                }
            } else if AuthError != nil {
                Async.main {
                    self.dismissProgressDialog {
                        self.view.makeToast(AuthError?.errors?.error_text)
                    }
                }
            } else if ServerKeyError != nil {
                Async.main {
                    self.dismissProgressDialog {
                        self.view.makeToast(ServerKeyError?.errors?.error_text)
                    }
                }
            } else {
                Async.main {
                    self.dismissProgressDialog {
                        self.view.makeToast(error?.localizedDescription)
                    }
                }
            }
        }
    }
    
    func checkForAgoraCallAction(call_id: String) {
        if Connectivity.isConnectedToNetwork() {
            guard self.callDeclinedCount < 6 else {
                self.closeAgoraCall(call_id: call_id)
                return
            }
            Async.background {
                CallManager.instance.checkForAgoraCall(access_token: AppInstance.instance._sessionId, call_id: call_id) { (success, sessionError, serverError, error) in
                    if let success = success {
                        Async.main {
                            self.dismissProgressDialog {
                                switch success._callStatusObject {
                                case .answered:
                                    self.checkCallTimer.invalidate()
                                    self.callDeclinedTimer.invalidate()
                                case .declined, .no_answer:
                                    self.closeScreen()
                                    break
                                case .calling:
                                    if !self.callDeclinedTimer.isValid {
                                        self.callDeclinedTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.incrementCallDeclinedCount), userInfo: nil, repeats: true)
                                    }
                                    break
                                case .unknown:
                                    break
                                }
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                }
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    func checkForTwilioCallAction(call_id: String, call_type: String) {
        if Connectivity.isConnectedToNetwork() {
            guard self.callDeclinedCount < 6 else {
                self.closeTwilioCall(call_id: call_id, call_type: call_type)
                return
            }
            Async.background {
                TwilloCallmanager.instance.checkForTwilloCall(access_token: AppInstance.instance._sessionId, call_id: call_id, call_type: call_type, completionBlock: { (success, sessionError, serverError, error) in
                    if let success = success {
                        Async.main {
                            self.dismissProgressDialog {
                                switch success._callStatusObject {
                                case .answered:
                                    self.checkCallTimer.invalidate()
                                    self.callDeclinedTimer.invalidate()
                                case .declined:
                                    self.closeScreen()
                                    break
                                case .calling, .no_answer:
                                    if !self.callDeclinedTimer.isValid {
                                        self.callDeclinedTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.incrementCallDeclinedCount), userInfo: nil, repeats: true)
                                    }
                                    break
                                case .unknown:
                                    break
                                }
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                })
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func answerAgoraCall(call_id: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                CallManager.instance.agoraCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "answer") { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                if success?.api_status == 200 {
                                    self.checkCallTimer.invalidate()
                                    self.callDeclinedTimer.invalidate()
                                    self.stopCallSound()
                                    switch self.callType {
                                    case .audio:
                                        self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Incoming", room_name: self.callData?.data?.room_name, call_type: "audio", type: "Answered")
                                        if let newVC = R.storyboard.call.agoraAudioCallVC() {
                                            newVC.incomingCallData = self.callData
                                            newVC.call_id = self.callData?.data?.id ?? ""
                                            newVC.room_name = self.callData?.data?.room_name ?? ""
                                            self.navigationController?.pushViewController(newVC, animated: true)
                                        }
                                    case .video:
                                        self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Incoming", room_name: self.callData?.data?.room_name, call_type: "video", type: "Answered")
                                        if let newVC = R.storyboard.call.agoraVideoCallVC() {
                                            newVC.incomingCallData = self.callData
                                            newVC.call_id = self.callData?.data?.id ?? ""
                                            newVC.room_name = self.callData?.data?.room_name ?? ""
                                            self.navigationController?.pushViewController(newVC, animated: true)
                                        }
                                    }
                                }
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                }
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func answerTwilioCall(call_id: String, call_type: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                TwilloCallmanager.instance.twilioCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "answer", call_type: call_type, completionBlock: { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                if success?.api_status == 200 {
                                    self.checkCallTimer.invalidate()
                                    self.callDeclinedTimer.invalidate()
                                    self.stopCallSound()
                                    switch self.callType {
                                    case .audio:
                                        self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Incoming", room_name: self.callData?.data?.room_name, call_type: "audio", type: "Answered")
                                        if let newVC = R.storyboard.call.twilioAudioCallVC() {
                                            newVC.incomingCallData = self.callData
                                            newVC.call_id = self.callData?.data?.id ?? ""
                                            newVC.room_name = self.callData?.data?.room_name ?? ""
                                            self.navigationController?.pushViewController(newVC, animated: true)
                                        }
                                    case .video:
                                        self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Incoming", room_name: self.callData?.data?.room_name, call_type: "video", type: "Answered")
                                        if let newVC = R.storyboard.call.twilioVideoCallVC() {
                                            newVC.incomingCallData = self.callData
                                            newVC.call_id = self.callData?.data?.id ?? ""
                                            newVC.room_name = self.callData?.data?.room_name ?? ""
                                            self.navigationController?.pushViewController(newVC, animated: true)
                                        }
                                    }
                                }
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                })
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func declineAgoraCall(call_id: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                CallManager.instance.agoraCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "decline") { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                print("declined")
                                switch self.callType {
                                case .audio:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "audio", type: "Cancel")
                                case .video:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "video", type: "Cancel")
                                }
                                self.closeScreen()
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                }
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func declineTwilioCall(call_id: String, call_type: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                TwilloCallmanager.instance.twilioCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "decline", call_type: call_type, completionBlock: { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                print("declined")
                                switch self.callType {
                                case .audio:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "audio", type: "Cancel")
                                case .video:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "video", type: "Cancel")
                                }
                                self.closeScreen()
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                })
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func closeAgoraCall(call_id: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                CallManager.instance.agoraCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "close") { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                print("close")
                                switch self.callType {
                                case .audio:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "audio", type: "NoAnswer")
                                case .video:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "video", type: "NoAnswer")
                                }
                                self.closeScreen()
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                }
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
    private func closeTwilioCall(call_id: String, call_type: String) {
        if Connectivity.isConnectedToNetwork() {
            Async.background {
                TwilloCallmanager.instance.twilioCallAction(access_token: AppInstance.instance._sessionId, call_id: call_id, action: "close", call_type: call_type, completionBlock: { (success, sessionError, serverError, error) in
                    if success != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                print("close")
                                switch self.callType {
                                case .audio:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "audio", type: "NoAnswer")
                                case .video:
                                    self.setCallLog(id: self.callData?.data?.id, user_id: self.callData?.user_id, avatar: self.callData?.avatar, name: self.callData?.name, from_id: self.callData?.data?.from_id, active: self.callData?.data?.active, time: Date().timeStamp(), status: "Missing", room_name: self.callData?.data?.room_name, call_type: "video", type: "NoAnswer")
                                }
                                self.closeScreen()
                            }
                        }
                    } else if sessionError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(sessionError?.errors?.error_text)
                            }
                        }
                    } else if serverError != nil {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(serverError?.errors?.error_text)
                            }
                        }
                    } else {
                        Async.main {
                            self.dismissProgressDialog {
                                self.view.makeToast(error?.localizedDescription)
                            }
                        }
                    }
                })
            }
        } else {
            self.dismissProgressDialog {
                self.view.makeToast(InterNetError)
            }
        }
    }
    
}

// MARK: CallMessageAlertVCDelegate
extension IncomingCallVC: CallMessageAlertVCDelegate {
    
    func handleCustomMessageButtonTap(sender: UIButton) {
        if let newVC = R.storyboard.popup.customMessageAlertVC() {
            newVC.delegate = self
            newVC.modalPresentationStyle = .overCurrentContext
            newVC.modalTransitionStyle = .crossDissolve
            self.present(newVC, animated: true)
        }
    }
    
    func handleCallMessageTap(message: String) {
        self.sendMessage(text: message)
    }
    
}

// MARK: CustomMessageAlertVCDelegate
extension IncomingCallVC: CustomMessageAlertVCDelegate {
    
    func handleCustomMessageSendButtonTap(sender: UIButton, text: String) {
        self.sendMessage(text: text)
    }
    
}
