//
//  SpeechModel.swift

//
//  Created by Leon on 2025/8/1.
//

import Foundation
import AVFoundation

// MARK: - Enums
enum SpeechPlayState: Int {
    case `default` // Default state
    case playing // Playing
    case complete // Playback completed
    case failed // Playback failed
}

enum SpeechLoadState: Int {
    case `default` // Default state
    case loading // Loading
    case complete // Loading completed
    case failed // Loading failed
}

enum EGAudioFileType: Int {
    case file // Local audio file
    case url // Remote audio file
    case base64 // Base64 encoded audio data
}

class SpeechModel: NSObject, AVAudioPlayerDelegate {
    // MARK: - Properties
    var playState: SpeechPlayState = .default
    var loadState: SpeechLoadState = .default
    var fileType: EGAudioFileType = .file
    var path: String = ""
    var canAutoPlay: Bool = false
    var stateChangedBlock: ((SpeechModel) -> Void)?
    var timerChangedBlock: ((TimeInterval) -> Void)?
    var progressChangedBlock: ((TimeInterval, TimeInterval) -> Void)? // 播放进度更新block (当前时间, 总时长)
    var audioDuration: TimeInterval = 0.0 // 音频总时长（秒）
    private var player: AVAudioPlayer?
    private var progressTimer: Timer?
    
    // MARK: - Computed Properties
    /// 时长 （秒x10)
    var audioTime: Int {
        guard let player = player else { return 0 }
        let isOtherPlaying = AVAudioSession.sharedInstance().isOtherAudioPlaying
        return Int(ceil(player.duration * 10)) + (isOtherPlaying ? 15 : 5)
    }
    
    var isPlaying: Bool {
        return player?.isPlaying ?? false
    }
    
    var currentTime: TimeInterval {
        get { player?.currentTime ?? 0 }
        set { player?.currentTime = newValue }
    }
    
    class func modelWith(path:String?) -> SpeechModel?{
        guard let pathValid = path else{
            return nil
        }
        let model = SpeechModel()
        model.path = pathValid
        model.fileType = .url
        return model
    }
    
    class func modelWithBase64String(_ base64String: String?) -> SpeechModel? {
        guard let base64Valid = base64String else {
            return nil
        }
        let model = SpeechModel()
        model.path = base64Valid
        model.fileType = .base64
        return model
    }
    
    // MARK: - Private Methods
    private func loadFileData() {
        guard !path.isEmpty else {
            dlog("❌ SpeechModel's file path is nil")
            setupDefault()
            return
        }
        
        loadState = .loading
        stateChanged()
        
        DispatchQueue.global().async { [weak self] in
            guard let self = self else { return }
            do {
                let audioData = try Data(contentsOf: URL(fileURLWithPath: self.path), options: .mappedIfSafe)
                self.player = try AVAudioPlayer(data: audioData)
                self.player?.delegate = self
                self.player?.prepareToPlay()
                self.audioDuration = self.player?.duration ?? 0.0
                self.loadState = .complete
                self.stateChanged()
            } catch {
                self.loadState = .failed
                self.stateChanged()
            }
        }
    }
    
    private func loadUrlData() {
        guard !path.isEmpty else {
            dlog("❌ SpeechModel's path is nil")
            setupDefault()
            return
        }
        
        loadState = .loading
        stateChanged()
        
        DispatchQueue.global().async { [weak self] in
            guard let self = self, let url = URL(string: self.path) else { return }
            do {
                let audioData = try Data(contentsOf: url, options: .mappedIfSafe)
                self.player = try AVAudioPlayer(data: audioData)
                self.player?.delegate = self
                self.player?.prepareToPlay()
                self.audioDuration = self.player?.duration ?? 0.0
                self.loadState = .complete
                self.stateChanged()
            } catch {
                self.loadState = .failed
                self.stateChanged()
            }
        }
    }
    
    private func loadBase64Data() {
        guard !path.isEmpty else {
            dlog("❌ SpeechModel's base64 string is nil")
            setupDefault()
            return
        }
        
        loadState = .loading
        stateChanged()
        
        DispatchQueue.global().async { [weak self] in
            guard let self = self else { return }
            do {
                guard let audioData = Data(base64Encoded: self.path) else {
                    DispatchQueue.main.async {
                        self.loadState = .failed
                        self.stateChanged()
                    }
                    return
                }
                
                self.player = try AVAudioPlayer(data: audioData)
                self.player?.delegate = self
                self.player?.prepareToPlay()
                self.audioDuration = self.player?.duration ?? 0.0
                self.loadState = .complete
                self.stateChanged()
            } catch {
                self.loadState = .failed
                self.stateChanged()
            }
        }
    }
    
    private func stateChanged() {
        DispatchQueue.main.async { [weak self] in
            guard let self = self, let block = self.stateChangedBlock else { return }
            block(self)
        }
    }
    
    private func checkPlayState() -> Bool {
        return player != nil
    }
    
    private func setupDefault() {
        playState = .default
        loadState = .default
        audioDuration = 0.0
        stateChanged()
    }
    
    // MARK: - Progress Update Methods
    private func startProgressUpdate() {
        stopProgressUpdate()
        progressTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
            self?.updateProgress()
        }
    }
    
    private func stopProgressUpdate() {
        progressTimer?.invalidate()
        progressTimer = nil
    }
    
    private func updateProgress() {
        guard let player = player, player.isPlaying else { return }
        let currentTime = player.currentTime
        let duration = player.duration
        
        DispatchQueue.main.async { [weak self] in
            guard let self = self, let block = self.progressChangedBlock else { return }
            block(currentTime, duration)
        }
    }
    
    // MARK: - Public Methods
    
    func refreshPath(path: String){
        self.path = path
    }
    
    func loadSpeechFile() {
        guard !path.isEmpty else {
            setupDefault()
            return
        }
        
        guard loadState != .loading else { return }
        
        if player != nil {
            loadState = .complete
            stateChanged()
            return
        }
        
        switch fileType {
        case .file:
            loadFileData()
        case .url:
            loadUrlData()
        case .base64:
            loadBase64Data()
        }
    }
    
    func startPlay() {
        guard checkPlayState() else { return }
        guard let player = player, !player.isPlaying else { return }
        
        do {
            let session = AVAudioSession.sharedInstance()
            try session.setCategory(.playAndRecord, mode: .default, options: [
                .allowBluetooth,
                .defaultToSpeaker,
                .allowAirPlay,
                .duckOthers,
                .mixWithOthers
            ])
            try session.setActive(true, options: [])
            player.currentTime = 0
            player.play()
            playState = .playing
            startProgressUpdate()
            stateChanged()
        } catch {
            playState = .failed
            stateChanged()
        }
    }
    
    func stopPlay() {
        guard checkPlayState() else {
            setupDefault()
            return
        }
        if player?.isPlaying == true {
            player?.stop()
            playState = .complete
            stateChanged()
            stopProgressUpdate()
            // 可选：停用会话，避免占用音频通道
            try? AVAudioSession.sharedInstance().setActive(false, options: [.notifyOthersOnDeactivation])
        }
    }
    
    func needLoadData() -> Bool {
        return player == nil && loadState != .loading
    }
    
    // MARK: - AVAudioPlayerDelegate
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        dlog("audioPlayerDidFinishPlaying")
        playState = .complete
        stateChanged()
        stopProgressUpdate()
    }
    
    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        dlog("audioPlayerDecodeErrorDidOccur")
        playState = .failed
        stateChanged()
        stopProgressUpdate()
    }
    
    // MARK: - Deinit
    deinit {
        print("♻️ SpeechModel dealloc")
        if player?.isPlaying == true {
            player?.stop()
        }
        stopProgressUpdate()
    }
}

/*
 使用示例：
 
 // 创建 SpeechModel 实例
 let speechModel = SpeechModel.modelWith(path: "audio_url")!
 
 // 设置播放进度更新回调
 speechModel.progressChangedBlock = { currentTime, totalDuration in
     let progress = currentTime / totalDuration
     print("播放进度: \(Int(currentTime))s / \(Int(totalDuration))s (\(Int(progress * 100))%)")
 }
 
 // 加载音频文件
 speechModel.loadSpeechFile()
 
 // 开始播放
 speechModel.startPlay()
 
 // 获取音频总时长
 let duration = speechModel.audioDuration
 print("音频总时长: \(duration) 秒")
 
 // 停止播放
 speechModel.stopPlay()
 */
