//
//  AgoraVideoCallVC.swift
//  WoWonder
//
//  Created by iMac on 03/10/23.
//  Copyright © 2023 ScriptSun. All rights reserved.
//

import UIKit
import AgoraRtcKit
import SDWebImage
import Async
import Toast_Swift
import WowonderMessengerSDK
import AVFoundation

class AgoraVideoCallVC: BaseVC {
    
    // MARK: - IBOutlets
    
    @IBOutlet weak var userNameLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var remoteVideoView: UIView!
    @IBOutlet weak var userImageView: UIImageView!
    @IBOutlet weak var localVideoView: UIView!
    @IBOutlet weak var localVideoMutedView: UIView!
    @IBOutlet weak var localAudioMutedIndicator: UIImageView!
    @IBOutlet weak var localVideoMutedIndicator: UIImageView!
    @IBOutlet weak var remoteAudioMutedIndicator: UIImageView!
    @IBOutlet weak var controlView: UIView!
    
    // MARK: - Properties
    
    var incomingCallData: UserCallData?
    var outgoingCallData: AgoraCallSuccessModel?
    var userData: UserData?
    var room_name = ""
    var call_id = ""
    var agoraKit: AgoraRtcEngineKit!
    private var callTimer = Timer()
    private var checkCallTimer = Timer()
    private var callOutgoingCount = 0
    private var callDeclinedTimer = Timer()
    private var seconds = 0
    private var callAudioPlayer: AVAudioPlayer?
    private var isJoined = false
    
    // MARK: - View Life Cycles
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        self.initialConfig()
    }
    
    // MARK: - Selectors
    
    // Back Button Action
    @IBAction func backButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        self.declineCall(call_id: self.call_id)
    }
    
    // Switch Camera Button Action
    @IBAction func switchCameraButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        sender.isSelected = !sender.isSelected
        self.agoraKit.switchCamera()
        self.resetHideButtonsTimer()
    }
    
    // Video Mute Button Action
    @IBAction func videoMuteButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        sender.isSelected = !sender.isSelected
        self.agoraKit.muteLocalVideoStream(sender.isSelected)
        self.localVideoView.isHidden = sender.isSelected
        self.localVideoMutedView.isHidden = !sender.isSelected
        self.localVideoMutedIndicator.isHidden = !sender.isSelected
        self.resetHideButtonsTimer()
    }
        
    // Audio Mute Button Action
    @IBAction func audioMuteButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        sender.isSelected = !sender.isSelected
        self.agoraKit.muteLocalAudioStream(sender.isSelected)
        self.localAudioMutedIndicator.isHidden = !sender.isSelected
        self.resetHideButtonsTimer()
    }
    
    // Hangup Button Action
    @IBAction func hangupButtonAction(_ sender: UIButton) {
        self.view.endEditing(true)
        self.declineCall(call_id: self.call_id)
    }
    
    // MARK: - Helper Functions
    
    // Initial Config
    func initialConfig() {
        self.setData()
        self.initializeAgoraEngine()
        self.setupLocalVideo()
        self.hideVideoMuted()
        self.setupButtons()
        self.joinChannel()
    }
    
    // Set Data
    func setData() {
        if let callData = self.incomingCallData {
            let url = URL(string: 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 = callData.name ?? ""
            self.descriptionLabel.text = "Waiting for answer"
        } else {
            self.playCallSound()
            self.checkCallTimer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(self.checkOutgoingCall), userInfo: nil, repeats: true)
            let url = URL(string: self.userData?.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.userData?.name ?? ""
            self.descriptionLabel.text = "Calling"
        }
    }
    
    func initializeAgoraEngine() {
        let config = AgoraRtcEngineConfig()
        config.appId = AppInstance.instance.siteSetting?.agora_chat_app_id
        self.agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
        self.agoraKit.setDefaultAudioRouteToSpeakerphone(true)
    }
    
    func joinChannel() {
        let option = AgoraRtcChannelMediaOptions()
        option.clientRoleType = .broadcaster
        option.channelProfile = .communication
        if let token = self.incomingCallData?.data?.access_token {
            self.agoraKit.joinChannel(byToken: token, channelId: self.room_name, uid: 0, mediaOptions: option) { (channel, uid, elapsed) in
                UIApplication.shared.isIdleTimerDisabled = true
            }
        } else {
            self.agoraKit.joinChannel(byToken: self.outgoingCallData?.token, channelId: self.room_name, uid: 0, mediaOptions: option) { (channel, uid, elapsed) in
                UIApplication.shared.isIdleTimerDisabled = true
            }
        }
    }
    
    func setupLocalVideo() {
        self.agoraKit.enableVideo()
        self.agoraKit.startPreview()
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = 0
        videoCanvas.renderMode = .hidden
        videoCanvas.view = self.localVideoView
        self.agoraKit.setupLocalVideo(videoCanvas)
    }
    
    private func invalidateCallTimers() {
        self.checkCallTimer.invalidate()
        self.callDeclinedTimer.invalidate()
    }
    
    private func closeScreen() {
        self.invalidateCallTimers()
        self.callTimer.invalidate()
        self.stopCallSound()
        self.navigationController?.popToRootViewController(animated: true)
    }
    
    func leaveChannel() {
        if self.agoraKit != nil {
            self.callTimer.invalidate()
            self.agoraKit.stopPreview()
            self.agoraKit.leaveChannel(nil)
            self.hideControlView()
            UIApplication.shared.isIdleTimerDisabled = false
            self.remoteVideoView.removeFromSuperview()
            self.localVideoView.removeFromSuperview()
            self.agoraKit = nil
        }
    }
    
    func hideVideoMuted() {
        self.remoteVideoView.isHidden = true
        self.localVideoMutedView.isHidden = true
        self.localAudioMutedIndicator.isHidden = true
        self.localVideoMutedIndicator.isHidden = true
        self.remoteAudioMutedIndicator.isHidden = true
    }
    
    func setupButtons() {
        perform(#selector(self.hideControlView), with:nil, afterDelay: 8)
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.viewTapped))
        view.addGestureRecognizer(tapGestureRecognizer)
        view.isUserInteractionEnabled = true
    }
    
    @objc func hideControlView() {
        self.controlView.isHidden = true
    }
    
    @objc func viewTapped() {
        if (self.controlView.isHidden) {
            self.controlView.isHidden = false;
            perform(#selector(self.hideControlView), with: nil, afterDelay: 8)
        } else {
            self.controlView.isHidden = true;
        }
    }
    
    private func resetHideButtonsTimer() {
        AgoraVideoCallVC.cancelPreviousPerformRequests(withTarget: self)
        perform(#selector(self.hideControlView), with: nil, afterDelay: 8)
    }
    
    private func onAnswerCall() {
        self.isJoined = true
        self.checkCallTimer.invalidate()
        self.callDeclinedTimer.invalidate()
        self.stopCallSound()
        self.callTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.setCallTimerToLabel(_:)), userInfo: nil, repeats: true)
        if self.incomingCallData == nil {
            self.setCallLog(id: String(self.outgoingCallData?.id ?? 0), user_id: self.userData?.user_id, avatar: self.userData?.avatar, name: self.userData?.name, from_id: AppInstance.instance.userId, active: self.userData?.active, time: Date().timeStamp(), status: "Outgoing", room_name: self.outgoingCallData?.room_name, call_type: "video", type: "Answered")
        }
    }
    
    @objc private func setCallTimerToLabel(_ timer: Timer) {
        self.seconds += 1
        self.descriptionLabel.text = self.getSecondsString()
    }
    
    private func getSecondsString() -> String {
        let hours = self.seconds / 3600
        let minutes = self.seconds / 60 % 60
        let seconds = self.seconds % 60
        return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
    }
    
    @objc private func checkOutgoingCall() {
        self.checkForCallAction(call_id: self.call_id)
    }
    
    @objc private func incrementOutgoingCallDeclinedCount() {
        self.callOutgoingCount += 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 AgoraVideoCallVC {
    
    func checkForCallAction(call_id: String) {
        if Connectivity.isConnectedToNetwork() {
            guard self.callOutgoingCount < 6 else {
                self.declineCall(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.invalidateCallTimers()
                                case .declined, .no_answer:
                                    if self.incomingCallData == nil {
                                        self.setCallLog(id: String(self.outgoingCallData?.id ?? 0), user_id: self.userData?.user_id, avatar: self.userData?.avatar, name: self.userData?.name, from_id: AppInstance.instance.userId, active: self.userData?.active, time: Date().timeStamp(), status: "Missing", room_name: self.outgoingCallData?.room_name, call_type: "video", type: "NoAnswer")
                                    }
                                    self.leaveChannel()
                                    self.closeScreen()
                                    break
                                case .calling:
                                    if !self.callDeclinedTimer.isValid {
                                        self.callDeclinedTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.incrementOutgoingCallDeclinedCount), 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 declineCall(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")
                                if self.incomingCallData == nil {
                                    if !self.isJoined {
                                        /*self.setCallLog(id: String(self.outgoingCallData?.id ?? 0), user_id: self.userData?.user_id, avatar: self.userData?.avatar, name: self.userData?.name, from_id: AppInstance.instance.userId, active: self.userData?.active, time: Date().timeStamp(), status: "Outgoing", room_name: self.outgoingCallData?.room_name, call_type: "video", type: "Cancel")
                                    } else {*/
                                        self.setCallLog(id: String(self.outgoingCallData?.id ?? 0), user_id: self.userData?.user_id, avatar: self.userData?.avatar, name: self.userData?.name, from_id: AppInstance.instance.userId, active: self.userData?.active, time: Date().timeStamp(), status: "Missing", room_name: self.outgoingCallData?.room_name, call_type: "video", type: "NoAnswer")
                                    }
                                }
                                self.leaveChannel()
                                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: AgoraRtcEngineDelegate Methods
extension AgoraVideoCallVC: AgoraRtcEngineDelegate {
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
        if (self.remoteVideoView.isHidden) {
            self.userImageView.isHidden = true
            self.remoteVideoView.isHidden = false
        }
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = uid
        videoCanvas.renderMode = .hidden
        videoCanvas.view = self.remoteVideoView
        self.agoraKit.setupRemoteVideo(videoCanvas)
        self.onAnswerCall()
    }
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
        self.leaveChannel()
        self.closeScreen()
    }
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didVideoMuted muted: Bool, byUid uid: UInt) {
        self.remoteVideoView.isHidden = muted
        self.userImageView.isHidden = !muted
    }
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didAudioMuted muted: Bool, byUid uid: UInt) {
        self.remoteAudioMutedIndicator.isHidden = !muted
    }
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
        print("errorCode >>>>>", errorCode)
    }
    
}
