//
//  CreatorCommunitySoundManager.swift
//  SodaLive
//
//  Created by klaus on 8/7/24.
//

import Foundation
import AVKit
import Combine

class CreatorCommunitySoundManager: NSObject, ObservableObject {
    enum RecordMode {
        case RECORD, PLAY
    }
    
    @Published var recordMode = RecordMode.RECORD
    
    @Published var errorMessage = ""
    @Published var isShowPopup = false
    @Published var isLoading = false
    @Published var onClose = false
    
    @Published var isPlaying = false
    @Published var isRecording = false
    
    @Published var timeString = "00:00.00"
    
    private var timerSubscription: Cancellable?
    private var startTime: Date?
    
    var player: AVAudioPlayer!
    var audioRecorder: AVAudioRecorder!
    
    var fileName = "now_voice.m4a"
    let audioSession = AVAudioSession.sharedInstance()
    
    func prepareRecording() {
        isLoading = true
        
        do {
            try audioSession.setCategory(.playAndRecord, mode: .videoRecording)
            try audioSession.setActive(true)
            audioSession.requestRecordPermission() { [weak self] allowed in
                DispatchQueue.main.async {
                    if !allowed {
                        self?.errorMessage = "권한을 허용하지 않으시면 음성녹음을 하실 수 없습니다."
                        self?.isShowPopup = true
                        self?.onClose = true
                    }
                }
            }
        } catch {
            errorMessage = "오류가 발생했습니다. 다시 시도해 주세요."
            isShowPopup = true
            onClose = true
        }
        
        isLoading = false
    }
    
    func startRecording(_ fileName: String) {
        self.fileName = fileName
        
        player?.stop()
        player = nil
        
        isLoading = true
        let settings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 48000,
            AVEncoderBitRateKey: 256000,
            AVNumberOfChannelsKey: 2,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]
        
        do {
            try audioSession.setCategory(.playAndRecord, mode: .videoRecording)
            try audioSession.setActive(true)
            
            audioRecorder = try AVAudioRecorder(url: getAudioFileURL(), settings: settings)
            audioRecorder.record()
            isRecording = true
            
            startTimer()
        } catch {
            errorMessage = "오류가 발생했습니다. 다시 시도해 주세요."
            isShowPopup = true
        }
        isLoading = false
    }
    
    func stopRecording() {
        stopTimer()
        
        audioRecorder?.stop()
        audioRecorder = nil
        isRecording = false
        prepareForPlay()
    }
    
    func prepareForPlay(_ url: URL? = nil) {
        isLoading = true
        
        DispatchQueue.main.async {
            do {
                try self.audioSession.setCategory(.playback, mode: .moviePlayback)
                try self.audioSession.setActive(true)
                
                if let url = url {
                    self.player = try AVAudioPlayer(data: Data(contentsOf: url))
                } else {
                    self.player = try AVAudioPlayer(contentsOf: self.getAudioFileURL())
                }
                
                self.player?.volume = 1
                self.player?.delegate = self
                self.player?.prepareToPlay()
            } catch {
                self.errorMessage = "오류가 발생했습니다. 다시 시도해 주세요."
                self.isShowPopup = true
            }
            
            self.isLoading = false
        }
    }
    
    func playAudio() {
        player?.play()
        
        isPlaying = player.isPlaying
        startTimer()
    }
    
    func stopAudio() {
        stopTimer()
        player?.stop()
        player.currentTime = 0
        isPlaying = player.isPlaying
    }
    
    func deleteAudioFile() {
        do {
            try FileManager.default.removeItem(at: getAudioFileURL())
        } catch {}
    }
    
    func getAudioFileURL() -> URL {
        return getDocumentsDirectory().appendingPathComponent(fileName)
    }
    
    private func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
    
    func startTimer() {
        timeString = "00:00.00"
        startTime = Date()
        timerSubscription = Timer.publish(every: 0.01, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.updateTime()
            }
    }
    
    func stopTimer() {
        timeString = "00:00.00"
        timerSubscription?.cancel()
        
        startTime = nil
        timerSubscription = nil
    }
    
    private func updateTime() {
        guard let startTime = startTime else { return }
        
        let elapsedTime = Date().timeIntervalSince(startTime)
        
        let minutes = Int(elapsedTime) / 60
        let seconds = Int(elapsedTime) % 60
        let centiseconds = Int((elapsedTime - Double(minutes * 60) - Double(seconds)) * 100)
        
        timeString = String(format: "%02d:%02d.%02d", minutes, seconds, centiseconds)
        
        if minutes >= 3 {
            stopRecording()
            recordMode = .PLAY
        }
    }
    
    deinit {
        player?.stop()
        audioRecorder?.stop()
        
        startTime = nil
        timerSubscription?.cancel()
    }
}

extension CreatorCommunitySoundManager: AVAudioPlayerDelegate {
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        stopAudio()
    }
}
