//
//  LNVoiceRecorder.swift
//  Lanu
//
//  Created by OneeChan on 2025/12/4.
//

import Foundation
import AVFAudio


enum LNIMChatVoiceRecorderState {
    case ready
    case recording
    case pausing
}


protocol LNVoiceRecorderNotify {
    func onRecordTaskDurationChanged(taskId: String, duration: Double, volumeRatio: Double)
    func onRecordTaskRecording(taskId: String)
    func onRecordTaskPause(taskId: String)
    func onRecordTaskStop(taskId: String)
    func onRecordTaskReachMaxDuration(taskId: String, fileUrl: URL?, duration: Double)
}
extension LNVoiceRecorderNotify {
    func onRecordTaskDurationChanged(taskId: String, duration: Double, volumeRatio: Double) { }
    func onRecordTaskRecording(taskId: String) { }
    func onRecordTaskPause(taskId: String) { }
    func onRecordTaskStop(taskId: String) { }
    func onRecordTaskReachMaxDuration(taskId: String, fileUrl: URL?, duration: Double) { }
}


class LNVoiceRecorder {
    static let shared = LNVoiceRecorder()
    
    private(set) var curState: LNIMChatVoiceRecorderState = .ready
    var isRecording: Bool {
        curState == .recording
    }
    private var recorder: AVAudioRecorder?
    
    private var durationTimer: Timer?
    private let timerInterval = 0.1
    private var curDuration: Double = 0.0
    private var curTaskId: String = ""
    private var maxDuration: Double = -1
    
    private init() { }
    
    func startRecord(_ maxDuration: Double? = nil, handler: @escaping (String?) -> Void) {
        guard curState == .ready,
              setupNewRecorder(),
              recorder?.record() == true
        else {
            handler(nil)
            return
        }
        if let maxDuration {
            self.maxDuration = maxDuration
        } else {
            self.maxDuration = -1
        }
        
        startDurationTimer()
        curState = .recording
        curTaskId = "\(curTime)"
        
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            notifyTaskStart()
        }
        handler(curTaskId)
    }
    
    func pauseRecord() {
        guard curState == .recording else { return }
        recorder?.pause()
        stopDurationTimer()
        curState = .pausing
        
        notifyTaskPause()
    }
    
    func continueRecord() {
        guard curState == .pausing else { return }
        if maxDuration != -1,
           maxDuration <= curDuration {
            let result = stopRecord()
            notifyTaskReachMaxDuration(file: result.0, duration: result.1)
            return
        }
            
        recorder?.record()
        startDurationTimer(true)
        curState = .recording
        
        notifyTaskStart()
    }
    
    @discardableResult
    func stopRecord() -> (URL?, Double) {
        recorder?.stop()
        try? AVAudioSession.sharedInstance().setActive(false)
        stopDurationTimer()
        curState = .ready
        
        let filePath = recorder?.url
        notifyTaskStop()
        
        curTaskId = ""
        
        return (filePath, curDuration)
    }
}

extension LNVoiceRecorder {
    private func setupNewRecorder() -> Bool {
        recorder?.stop()
        try? AVAudioSession.sharedInstance().setActive(false)
        recorder?.deleteRecording()
        
        do {
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
            
            // 录音参数配置（M4A 格式，高质量）
            let recordSettings: [String: Any] = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC), // 音频格式（AAC）
                AVSampleRateKey: 16000.0, // 采样率
                AVNumberOfChannelsKey: 1, // 声道数（立体声）
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue, // 音质
                AVLinearPCMBitDepthKey: 16
            ]
            
            // 创建录音器
            let recorder = try AVAudioRecorder(url: newVoicePath(), settings: recordSettings)
            recorder.isMeteringEnabled = true // 开启音量监测
            recorder.prepareToRecord()
            
            self.recorder = recorder
            curState = .ready
            return true
        } catch {
            Log.e("录音器初始化失败：\(error.localizedDescription)")
        }
        return false
    }
    
    func newVoicePath() -> URL {
        URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(UUID().uuidString)__voice_record.m4a")
    }
}

extension LNVoiceRecorder {
    private func startDurationTimer(_ isContinue: Bool = false) {
        stopDurationTimer()
        if !isContinue {
            curDuration = 0
            notifyTaskProgress(duration: curDuration, volumeRatio: 0)
        }
        let timer = Timer.scheduledTimer(withTimeInterval: timerInterval, repeats: true, block: { [weak self] _ in
            guard let self else { return }
            curDuration = recorder?.currentTime ?? 0
            
            var volumeRatio: Double = 0
            if let recorder {
                recorder.updateMeters()
                let decibel = Double(recorder.averagePower(forChannel: 0))
                volumeRatio = max(0.0, min(1.0, (decibel + 160.0) / 160.0))
            }
            notifyTaskProgress(duration: curDuration, volumeRatio: volumeRatio)
            
            if maxDuration != -1,
               maxDuration <= curDuration {
                let result = stopRecord()
                notifyTaskReachMaxDuration(file: result.0, duration: result.1)
            }
        })
        timer.tolerance = 0.1 // 允许轻微延迟，提升性能
        RunLoop.current.add(timer, forMode: .common)
        durationTimer = timer
    }
    
    private func stopDurationTimer() {
        durationTimer?.invalidate()
        durationTimer = nil
    }
}

extension LNVoiceRecorder {
    private func notifyTaskProgress(duration: Double, volumeRatio: Double) {
        let taskId = curTaskId
        LNEventDeliver.notifyEvent {
            ($0 as? LNVoiceRecorderNotify)?.onRecordTaskDurationChanged(taskId: taskId, duration: duration, volumeRatio: volumeRatio)
        }
    }
    
    private func notifyTaskStop() {
        let taskId = curTaskId
        LNEventDeliver.notifyEvent {
            ($0 as? LNVoiceRecorderNotify)?.onRecordTaskStop(taskId: taskId)
        }
    }
    
    private func notifyTaskStart() {
        let taskId = curTaskId
        LNEventDeliver.notifyEvent {
            ($0 as? LNVoiceRecorderNotify)?.onRecordTaskRecording(taskId: taskId)
        }
    }
    
    private func notifyTaskPause() {
        let taskId = curTaskId
        LNEventDeliver.notifyEvent {
            ($0 as? LNVoiceRecorderNotify)?.onRecordTaskPause(taskId: taskId)
        }
    }
    
    private func notifyTaskReachMaxDuration(file: URL?, duration: Double) {
        let taskId = curTaskId
        LNEventDeliver.notifyEvent {
            ($0 as? LNVoiceRecorderNotify)?.onRecordTaskReachMaxDuration(taskId: taskId, fileUrl: file, duration: duration)
        }
    }
}
