//
// © 2018-2020 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
//

import UIKit

class AVSOnboardingShowCodeViewController: ModalViewController {
    var deviceID: String?

    @IBOutlet weak var gotoAmazonButton: UIButton!
    @IBOutlet weak var codeLabel: UILabel!
    @IBOutlet weak var urlLabel: UILabel!
    @IBOutlet weak var stackView: UIStackView!

    weak var codeTimer: Timer?
    var observers = [ObserverToken] ()

    var authInfo: VoiceUIAuthenticationInfo? = nil
}

extension AVSOnboardingShowCodeViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        gotoAmazonButton.makeAccessible(Accessibility.AVSOnboardingShowCodeScreen.gotoAmazonButton)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        codeLabel.isHidden = true
        urlLabel.isHidden = true
        stackView.isHidden = true
        gotoAmazonButton.isEnabled = false

        setUpObservers()

        if let deviceID = deviceID,
            let device = playerController.device(id: deviceID) {
            device.startAVSOnboarding(completion: { [weak self] (success, error) in
                guard let self = self else {
                    return
                }
                if success {
                    self.waitForCode()
                }
            }, completionQueue: DispatchQueue.main)
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        codeTimer?.invalidate()
        codeTimer = nil
        removeObservers()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destVC = segue.destination as? AVSOnboardingWebViewViewController {
            destVC.url = authInfo?.url
            destVC.deviceCode = authInfo?.code
            destVC.injectDependencies(preferences: preferences, playerController: playerController)
        }
    }
}

extension AVSOnboardingShowCodeViewController {
    private func setUpObservers() {
        let deviceHandler: (DeviceNotification) -> () = { [weak self] notification in
            guard let self = self else { return }

            if notification.device.deviceID == self.deviceID! &&
                notification.reason == .removed {
                self.codeTimer?.invalidate()
                self.codeTimer = nil
                appSpinnerPresenter()?.dismissWaitSpinner()
                self.presentingViewController?.dismiss(animated: true, completion: nil)
            }
        }

        let deviceObserver = NotificationCenter.default.addObserver(forType: DeviceNotification.self,
                                                                    object: playerController,
                                                                    queue: OperationQueue.main,
                                                                    using: deviceHandler)
        // ************


        let voiceUIHandler: (VoiceUINotification) -> () = { [weak self] notification in
            guard let self = self else { return }
            if notification.device.deviceID == self.deviceID! {
                if notification.reason == .authenticationInfoGenerated,
                    let payload = notification.payload as? VoiceUIAuthenticationInfo {
                    self.receivedAuthInfo(payload)
                } else if notification.reason == .authenticationError,
                    let timer = self.codeTimer {
                    self.codeTimer = nil
                    timer.invalidate()
                    self.didError()
                }
            }
        }

        let voiceUIObserver = NotificationCenter.default.addObserver(forType: VoiceUINotification.self,
                                                                     object: playerController,
                                                                     queue: OperationQueue.main,
                                                                     using: voiceUIHandler)

        observers = [deviceObserver, voiceUIObserver]
    }

    private func removeObservers () {
        observers.forEach {
            NotificationCenter.default.removeObserver($0)
        }
        observers.removeAll()
    }

    private func waitForCode() {
        if authInfo == nil {
            let timer = Timer(timeInterval: 30,
                              target: self,
                              selector: #selector(codeTimerExpired(_:)),
                              userInfo: nil,
                              repeats: false)
            RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
            codeTimer = timer
            appSpinnerPresenter()?.presentWaitSpinner()
        }
    }

    @objc private func codeTimerExpired(_ timer: Timer) {
		didError()
    }

    func didError () {
        // Small delay here as possible error will occur before wait spinner properly shown. Trying to present
        // alert then causes error.
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
            guard let self = self else {
                return
            }
            appSpinnerPresenter()?.dismissWaitSpinner()

            let alert = UIAlertController(title: NSLocalizedString("Speaker Cannot Be Added", comment: "AVS add fail title"),
                                          message: NSLocalizedString("Your speaker could not start the process to add itself to your Amazon account", comment: "AVS add fail message"),
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK"),
                                          style: .default,
                                          handler: { [weak self] _ in
                                            guard let self = self else {
                                                return
                                            }
                                            self.navigationController?.popViewController(animated: true)
            }))
            self.present(alert, animated: true)
        }
    }

    private func receivedAuthInfo(_ info: VoiceUIAuthenticationInfo) {
        authInfo = info
    	codeTimer?.invalidate()
        codeTimer = nil
    	appSpinnerPresenter()?.dismissWaitSpinner()

        if let deviceID = deviceID,
            let _ = playerController.device(id: deviceID) {
            codeLabel.isHidden = false
            urlLabel.isHidden = false
            stackView.isHidden = false
            codeLabel.text = info.code
            urlLabel.text = info.url
            gotoAmazonButton.isEnabled = true
        }
    }
}
