//
//  JWKaraokePlayerVM.swift
//  WOKA
//
//  Created by Bilal on 09/08/2024.
//

import UIKit
import AVFAudio
import AVFoundation
import GoogleMobileAds

class JWKaraokePlayerVM{
    
    weak var vc : JWKaraokePlayerVC!
    var mixedAudioURL: URL?
    var audioURLFromMP4 : URL?
    var recordedAudioURL: URL?
    var audioRecorder: AVAudioRecorder?
    var videoTitle : String?
    var videoUrl : String?
    
    //for mapping the post
    var postID : Int?
    
    //Start and end time for trimming the audio
    var startTime : TimeInterval?
    var endTime : TimeInterval?
    
    var startTimeStamp = Date()
    var headerBannerView = GADBannerView()

    func initView(){
        AdReusable.sharedInstance.setupBannerAd(bannerView: self.headerBannerView, in: vc.adView, adUnitID: K.GoogleAdIDs.themeTwo, viewController: self.vc)

        startTimeStamp = Date()
        vc.downloadRecordingBtn.isEnabled = false
        hideShowKaraoke(isLoading: true)
    }
    
    // MARK: - Document Directory Save & Fetch audio for recording

    func saveToFilesApp() {
        guard let mixedAudioURL = mixedAudioURL else { return }
        DispatchQueue.main.async {
            let documentPicker = UIDocumentPickerViewController(url: mixedAudioURL, in: .exportToService)
            documentPicker.delegate = self.vc
            self.vc.present(documentPicker, animated: true, completion: nil)
        }
    }
    
    func setupKaraoke(){
        guard let videoUrl else{return}
        let avURL = URL(string: "https://content.jwplatform.com/videos/699dmCGz-UwFX5S0R.mp4")!
        let asset = AVAsset(url: avURL)
        hideShowKaraoke(isLoading: true)
        let outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(videoTitle?.trimmingCharacters(in: .whitespaces) ?? "extractedAudio").m4a")
        FileManager.default.clearTmpDirectory()
        asset.writeAudioTrackToURL(outputURL) { [weak self] isDone, error,url in
            guard let self else{return}
            print(isDone, error , url)
            DispatchQueue.main.async {
                if error == nil && isDone{
                    self.hideShowKaraoke(isLoading: false)
                    self.audioURLFromMP4 = url
                    self.setupAudio()
                }else{
                    print("errrrrr", error)
                    self.vc.karaokeLoading.stopAnimating()
                    self.vc.karaokeStack.isHidden = true
                    self.vc.startStopRecordingStack.isHidden = true
                    
                    self.vc.retryKaraokeBtn.isHidden = false
                }
            }
        }
    }
    
    func hideShowKaraoke(isLoading : Bool){
        DispatchQueue.main.async { [weak self] in
            guard let self else{return}
            if isLoading{
                vc.karaokeLoading.startAnimating()
                vc.karaokeStack.isHidden = false
                vc.startStopRecordingStack.isHidden = true
            }else{
                vc.karaokeLoading.stopAnimating()
                vc.karaokeLoading.hidesWhenStopped = true
                vc.karaokeStack.isHidden = true
                vc.startStopRecordingStack.isHidden = false
            }
        }
    }
    
    // MARK: - App LifeCycle HAndler
    
    func updateKaraokeVideoView(){
        if let postID {
            let duration = DateFormatterLib.dateDifferenceINT(date1: startTimeStamp, date2: Date())
            AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.karaokeVideo.rawValue, duration: duration, catID: 0) { isDone in
                if isDone{
                    K.GVar.reloadContinueKaraoke = true
                }
            }
        }
    }
    
    // MARK: - AudioRecording
        
    func setupAudio() {
//        FileManager.default.clearTmpDirectory()

        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(.playAndRecord, mode: .default,options: .defaultToSpeaker)
            try audioSession.setActive(true)
            
//            // URL of the downloaded M4A file
//            guard let audioURL = Bundle.main.url(forResource: "Sample_audio", withExtension: "m4a") else {
//                print("Audio file not found.")
//                return
//            }
            
            // Initialize AVAudioPlayer with the downloaded M4A file
//            player = try AVAudioPlayer(contentsOf: audioURLFromMP4!)
//            player?.prepareToPlay()
            
            // Define settings for the audio recorder
            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let outputURL = documentsDirectory.appendingPathComponent("recordedAudio.m4a")
            let settings: [String: Any] = [
                AVFormatIDKey: kAudioFormatMPEG4AAC,
                AVSampleRateKey: 44100,
                AVNumberOfChannelsKey: 2,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]
            
            // Initialize AVAudioRecorder with the output URL and settings
            audioRecorder = try AVAudioRecorder(url: outputURL, settings: settings)
            audioRecorder?.prepareToRecord()
            recordedAudioURL = outputURL // Store the recorded audio URL
        } catch {
            print("Error setting up audio: \(error.localizedDescription)")
        }
    }
    
    func startRecording() {
        startTime = vc.player.time.position.round(to: 1)
        vc.interfaceBehavior = .hidden
        guard let audioRecorder = audioRecorder else { return }
        audioRecorder.record()
    }
    
    func stopRecording() {
        endTime = vc.player.time.position.round(to: 1)
        vc.interfaceBehavior = .normal
        guard let audioRecorder = audioRecorder else { return }
        audioRecorder.stop()
        vc.player.pause()
        // Mix the recorded audio with the downloaded M4A file
        guard let startTime , let endTime else{return}
        mixAudio(start: startTime, stop: endTime)
    }
    
    func playMixedAudio() {
        guard let mixedAudioURL = mixedAudioURL else { return }

        do {
            let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let destinationURL = documentsDirectoryURL.appendingPathComponent("xyze.m4a")
            
            // Check if file already exists
            if FileManager.default.fileExists(atPath: destinationURL.path) {
                // Delete the existing file
                try FileManager.default.removeItem(at: destinationURL)
            }
            
            // Copy the new file
            try FileManager.default.copyItem(at: mixedAudioURL, to: destinationURL)
            
            // Play the audio
            let playerKAraoke = AVPlayer(url: destinationURL)
            
            // Adding a completion handler to check if the player starts playing
            let playerObserver = playerKAraoke.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1), queue: .main) { time in
                print("Playing audio at time: \(time.seconds)")
            }
            
            // Observing when playback finishes
            NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: playerKAraoke.currentItem, queue: .main) { [weak self] _ in
                print("Audio finished playing")
                guard let self else{return}
                self.vc.playBtn.setTitle("Play", for: .normal)
                self.vc.playBtn.setImage(UIImage(named: "PlayButtonSmall"), for: .normal)
                
                // Disable Recording while playing, hide controls for jwplayer
                self.vc.interfaceBehavior = .normal
                self.vc.startRecordBtn.isEnabled = true
                self.vc.downloadRecordingBtn.isEnabled = true
                // Remove observer
                playerKAraoke.removeTimeObserver(playerObserver)
            }
            
            playerKAraoke.volume = 1.0
            playerKAraoke.play()
            print("Audio is playing...")
            // Uncomment this block if you need to configure the audio session and play using AVAudioPlayer
            /*
            // Configure the audio session
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
            try audioSession.setActive(true)

            let audioPlayer = try AVAudioPlayer(contentsOf: mixedAudioURL)
            audioPlayer.volume = 1.0
            audioPlayer.play()
            */
            
        } catch {
            print("Error: \(error.localizedDescription)")
        }
    }

    func mixAudio(start : TimeInterval, stop : TimeInterval) {
        let totalTime = stop - start
        guard let recordedAudioURL = recordedAudioURL else { return }
        guard let playerURL = audioURLFromMP4 else { return }
        Utilities.startProgressHUD(msg: "Mixing Audio")
        let composition = AVMutableComposition()
        let compositionDuration = CMTime(seconds: totalTime, preferredTimescale: 1)
        
        // Add the recorded audio from 0 to 10 seconds
        let recordedAudioAsset = AVURLAsset(url: recordedAudioURL)
        let recordedAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
        
        do {
            try recordedAudioTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: compositionDuration), of: recordedAudioAsset.tracks(withMediaType: .audio)[0], at: CMTime.zero)
        } catch {
            Utilities.dismissProgressHUD()
            print("Error adding recorded audio track: \(error.localizedDescription)")
        }
        
        // Add the downloaded M4A file from 10 to 20 seconds
        let playerAsset = AVURLAsset(url: playerURL)
        let playerTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
        
        do {
            try playerTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime(seconds: start, preferredTimescale: 1), duration: compositionDuration), of: playerAsset.tracks(withMediaType: .audio)[0], at: CMTime.zero)
        } catch {
            Utilities.dismissProgressHUD()
            print("Error adding player audio track: \(error.localizedDescription)")
        }
        
        // Example usage:

        
        // Export the mixed audio
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let fileName = "\((self.videoTitle?.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: ".", with: "") ?? "Audio") + Date().timeIntervalSince1970.toString()).m4a"
        let filePath = documentsDirectory.appendingPathComponent(fileName)
        deleteFileIfExist(at: filePath)

        mixedAudioURL = filePath
        
        guard let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A) else { return }
        exportSession.outputURL = mixedAudioURL
        exportSession.outputFileType = .m4a

        exportSession.exportAsynchronously {
            if exportSession.status == .completed {
                print("Mixing audio completed.")
                Utilities.dismissProgressHUD()
                DispatchQueue.main.async { [weak self] in
                    guard let self else{return}
                    vc.playBtn.isEnabled = true
                    vc.downloadRecordingBtn.isEnabled = true
                }
                
                
            } else if exportSession.status == .failed {
                print("Mixing audio failed.", exportSession.error?.localizedDescription)
            }
        }
    }
    
    func deleteFileIfExist(at url: URL) {
        let fileManager = FileManager.default
        
        do {
            // Check if the file exists
            if fileManager.fileExists(atPath: url.path) {
                // Attempt to delete the file
                try fileManager.removeItem(at: url)
                print("File deleted successfully.")
            } else {
                print("File does not exist at path: \(url.path)")
            }
        } catch {
            print("Error deleting file: \(error.localizedDescription)")
        }
    }
}
