//
//  AudioPlayTool.swift
//  LegendTeam
//
//  Created by dong on 2022/1/2.
//

import Foundation
import UIKit
import AVFAudio

enum VoicePlayViewState {
    case normal
    case loading
    case playing
}

class AudioPlayTool: NSObject {
    public var loadCompleteBlock: ((_ url: URL?) -> Void)?
    public var playFinishBlock: ((_ url: URL?) -> Void)?
    public var playProgressBlock: ((_ secconds: Int?) -> Void)?

    private(set) var audioPlayer: AVAudioPlayer?

    private(set) var playingUrl: URL?
    
    private(set) var state: VoicePlayViewState = .normal

    private var timer: Timer?

    private var allowPlay: Bool = false
    

    override init() {
        super.init()
        NotificationCenter.default.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
    }

    deinit {
        if let isPlaying = audioPlayer?.isPlaying, isPlaying == true {
            audioPlayer?.stop()
            audioPlayer = nil
        }
        NotificationCenter.default.removeObserver(self)
        print("♻️ dealloc audio player")
    }
    
    public class func audioChannelFreeToUse() -> Bool{
//        guard PhoneManager.isInPhoneChannel() == false && ChatRoomRoute.isChatRoomOpen() == false && PhoneManager.shared().isPlayingRing == false else{
//            UIWindow.key?.makeToast("Currently using other voice communication services, please try again later")
//            return false
//        }
        return true
    }

    // 网络或者在线
    public func load(url: URL?, complete: ((_ url: URL?) -> Void)?) {
        stop()
        
        allowPlay = true

        loadCompleteBlock = complete
        guard let audioUrl = url else {
            return
        }
        playingUrl = audioUrl
        state = .loading
        
        DispatchQueue.global().async { [weak self] in
            do {
                let data = try Data(contentsOf: url!)

                if let allow = self?.allowPlay, allow == true {
                    self?.audioPlayer = try AVAudioPlayer(data: data)
                    // try audioPlayer = AVAudioPlayer(contentsOf: audioUrl)
                    self?.audioPlayer?.delegate = self
                    self?.audioPlayer!.prepareToPlay()
                    if self?.loadCompleteBlock != nil {
                        DispatchQueue.main.async { [weak self] in
                            self?.loadCompleteBlock!(url)
                        }
                    }
                } else { // eg: 加载过程中，停止，应该直接返回
                    self?.stop()
                }

            } catch {}
        }
    }
    
    // 支持 base64 字符串初始化音频
    public func load(base64String: String?, complete: ((_ url: URL?) -> Void)?) {
        stop()
        
        allowPlay = true
        loadCompleteBlock = complete
        
        guard let base64Str = base64String, !base64Str.isEmpty else {
            complete?(nil)
            return
        }
        
        state = .loading
        
        DispatchQueue.global().async { [weak self] in
            do {
                guard let data = Data(base64Encoded: base64Str) else {
                    DispatchQueue.main.async { [weak self] in
                        self?.loadCompleteBlock?(nil)
                    }
                    return
                }
                
                if let allow = self?.allowPlay, allow == true {
                    self?.audioPlayer = try AVAudioPlayer(data: data)
                    self?.audioPlayer?.delegate = self
                    self?.audioPlayer!.prepareToPlay()
                    
                    if self?.loadCompleteBlock != nil {
                        DispatchQueue.main.async { [weak self] in
                            self?.loadCompleteBlock?(nil) // base64 没有 URL，传 nil
                        }
                    }
                } else { // eg: 加载过程中，停止，应该直接返回
                    self?.stop()
                }
                
            } catch {
                DispatchQueue.main.async { [weak self] in
                    self?.loadCompleteBlock?(nil)
                }
            }
        }
    }

    func isPlaying() -> Bool {
        audioPlayer?.isPlaying ?? false
    }

    public func play() {
        let session = AVAudioSession.sharedInstance()
        try? session.setCategory(AVAudioSession.Category.playAndRecord)
        try? session.overrideOutputAudioPort(.speaker)
        try? session.setActive(true)

        if playProgressBlock != nil {
            startTimer()
        }
        audioPlayer?.play()
        state = .playing
    }

    public func stop() {
        state = .normal
        if let isPlaying = audioPlayer?.isPlaying, isPlaying == true {
            audioPlayer?.stop()
            playFinishBlock?(playingUrl)
            clearTimer()
        }
        allowPlay = false
    }

    public func setupListenPlayProgress(block: ((_ secconds: Int?) -> Void)?) {
        playProgressBlock = block
    }

    // MARK: - helper

    private func clearTimer() {
        if timer != nil {
            timer?.invalidate()
            timer = nil
        }
    }

    private func startTimer() {
        clearTimer()
        let myTimer = Timer(timeInterval: 0.1, target: self, selector: #selector(timerDidChanged), userInfo: nil, repeats: true)
        RunLoop.current.add(myTimer, forMode: .common)
        myTimer.fire()
        timer = myTimer
    }

    // MARK: - action

    @objc private func timerDidChanged() {
        if let audiotime = audioPlayer?.currentTime, let duration = audioPlayer?.duration {
            let seconds = Int(ceil(duration - audiotime))

            // dlog("player's current time: \(String(describing: audioPlayer?.currentTime))  seconds: \(seconds)")
            playProgressBlock?(seconds)
        }
    }

    // MARK: - noti

    @objc private func enterBackground() {
        stop()
    }
}

// MARK: - AVAudioPlayerDelegate

extension AudioPlayTool: AVAudioPlayerDelegate {
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        state = .normal
        clearTimer()
        DispatchQueue.main.async { [weak self] in
            self?.playFinishBlock?(self?.playingUrl)
        }
    }

    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {}

    func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {}

    func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {}
}
