import AVFoundation
import Foundation
import FluidAudio

@MainActor
final class ParakeetSpeechRecognizer: ObservableObject {
    @Published var transcript: String = ""
    @Published var errorMessage: String?
    @Published private(set) var isRecording: Bool = false
    
    func clearTranscript() {
        guard !transcript.isEmpty else {
            print("⚠️  Transcript already empty, skipping clear")
            return
        }
        
        print("🗑️  Clearing transcript (immediate)")
        
        // If recording, also clear the sample buffer
        if isRecording {
            sampleBuffer.removeAll(keepingCapacity: true)
            print("🗑️  Also cleared recording buffer")
        }
        
        transcript = ""
        print("✅ Transcript cleared - now empty: \(transcript.isEmpty)")
    }
    
    private let audioEngine = AVAudioEngine()
    private var asrManager: AsrManager?
    private let modelManager = ModelManager.shared
    private var currentLoadedVersion: ParakeetModelVersion?
    
    private var sampleBuffer: [Float] = []
    private let targetSampleRate: Double = 16_000
    
    init() {
        // Don't auto-load model on init - wait for user to trigger recording
        
        // Listen for audio engine configuration changes (e.g., microphone switches)
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleEngineConfigurationChange(_:)),
            name: .AVAudioEngineConfigurationChange,
            object: audioEngine
        )
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    func ensureModelLoaded() async {
        // Check if we need to load a new model
        let selectedVersion = modelManager.selectedModelVersion
        
        if currentLoadedVersion == selectedVersion && asrManager != nil {
            // Already loaded correct model
            return
        }
        
        // Need to load/reload model
        await loadModel(version: selectedVersion)
    }
    
    private func loadModel(version: ParakeetModelVersion) async {
        do {
            modelManager.modelState = .downloading
            modelManager.downloadProgress = 0
            modelManager.errorMessage = "Downloading model files..."
            
            print("📥 Starting model download: \(version.displayName)")
            
            // Start progress animation
            let progressTask = Task { @MainActor in
                var progress: Double = 0
                while modelManager.modelState == .downloading {
                    progress += 0.01
                    if progress > 0.95 {
                        progress = 0.1 // Loop back but show activity
                    }
                    modelManager.downloadProgress = progress
                    try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
                }
            }
            
            let models = try await AsrModels.downloadAndLoad(version: version.asrVersion)
            progressTask.cancel()
            
            print("🔄 Model downloaded, initializing...")
            modelManager.errorMessage = "Initializing model..."
            
            asrManager = AsrManager()
            try await asrManager?.initialize(models: models)
            
            print("✅ Model ready: \(version.displayName)")
            currentLoadedVersion = version
            modelManager.modelState = .ready
            modelManager.downloadProgress = 1.0
            modelManager.errorMessage = nil
            errorMessage = nil
        } catch {
            print("❌ Model load failed: \(error)")
            modelManager.modelState = .error
            modelManager.errorMessage = "Failed to load model: \(error.localizedDescription)"
            errorMessage = "Failed to load model: \(error.localizedDescription)"
        }
    }
    
    func startRecording(lowercase: Bool) async {
        // Ensure correct model is loaded before recording
        await ensureModelLoaded()
        
        guard asrManager != nil else {
            errorMessage = "Model not loaded"
            return
        }
        
        guard await requestMicrophoneAccess() else {
            errorMessage = "Microphone permission denied"
            return
        }
        
        do {
            sampleBuffer.removeAll(keepingCapacity: true)
            installTap()
            audioEngine.prepare()
            try audioEngine.start()
            isRecording = true
            errorMessage = nil
            
            // Store lowercase preference for when we stop
            modelManager.shouldLowercase = lowercase
        } catch {
            errorMessage = "Failed to start recording: \(error.localizedDescription)"
        }
    }
    
    func stopRecording() async {
        guard isRecording else { return }
        
        // Stop audio immediately and update UI state
        audioEngine.inputNode.removeTap(onBus: 0)
        audioEngine.stop()
        isRecording = false
        
        // Force UI update
        objectWillChange.send()
        
        guard !sampleBuffer.isEmpty else { return }
        
        let samplesToTranscribe = sampleBuffer
        sampleBuffer.removeAll()
        
        // Process transcription in background
        do {
            guard let asrManager = asrManager else {
                errorMessage = "ASR not initialized"
                return
            }
            
            print("🎤 Transcribing audio...")
            let result = try await asrManager.transcribe(samplesToTranscribe, source: .microphone)
            var text = result.text.trimmingCharacters(in: .whitespacesAndNewlines)
            
            print("📝 Transcription result: \(text)")
            
            if modelManager.shouldLowercase {
                text = text.lowercased()
            }
            
            if !text.isEmpty {
                await MainActor.run {
                if transcript.isEmpty {
                    transcript = text
                } else {
                    transcript += " " + text
                }
                    print("✅ Transcript updated: \(transcript)")
                }
            } else {
                print("⚠️  Empty transcription result")
            }
            
            errorMessage = nil
        } catch {
            await MainActor.run {
            errorMessage = "Transcription failed: \(error.localizedDescription)"
            }
        }
    }
    
    private func requestMicrophoneAccess() async -> Bool {
        return await withCheckedContinuation { continuation in
            switch AVCaptureDevice.authorizationStatus(for: .audio) {
            case .authorized:
                continuation.resume(returning: true)
            case .notDetermined:
                AVCaptureDevice.requestAccess(for: .audio) { granted in
                    continuation.resume(returning: granted)
                }
            case .denied, .restricted:
                continuation.resume(returning: false)
            @unknown default:
                continuation.resume(returning: false)
            }
        }
    }
    
    private func installTap() {
        let input = audioEngine.inputNode
        let nativeFormat = input.outputFormat(forBus: 0)
        let nativeSampleRate = nativeFormat.sampleRate
        
        input.removeTap(onBus: 0)
        input.installTap(onBus: 0, bufferSize: 4096, format: nativeFormat) { [weak self] buffer, _ in
            guard let self = self else { return }
            Task { @MainActor in
                self.processAudioBuffer(buffer, sourceSampleRate: nativeSampleRate)
            }
        }
    }
    
    private func processAudioBuffer(_ buffer: AVAudioPCMBuffer, sourceSampleRate: Double) {
        guard let channelData = buffer.floatChannelData?.pointee else { return }
        let frameCount = Int(buffer.frameLength)
        
        // If source is already 16kHz, just copy
        if abs(sourceSampleRate - targetSampleRate) < 1.0 {
            for i in 0..<frameCount {
                sampleBuffer.append(channelData[i])
            }
            return
        }
        
        // Downsample from source rate to 16kHz using simple decimation
        let ratio = sourceSampleRate / targetSampleRate
        let outputFrameCount = Int(Double(frameCount) / ratio)
        
        for i in 0..<outputFrameCount {
            let sourceIndex = Int(Double(i) * ratio)
            if sourceIndex < frameCount {
                sampleBuffer.append(channelData[sourceIndex])
            }
        }
    }
    
    // MARK: - Audio Engine Resiliency
    
    @objc private func handleEngineConfigurationChange(_ notification: Notification) {
        guard isRecording else { return }
        print("🔄 Audio configuration changed (microphone switch detected)")
        resumeRecordingPipeline(reason: "microphone switched")
    }
    
    private func resumeRecordingPipeline(reason: String) {
        print("🔄 Resuming recording pipeline: \(reason)")
        
        // Stop engine but keep the sample buffer
        audioEngine.stop()
        audioEngine.reset()
        audioEngine.inputNode.removeTap(onBus: 0)
        
        do {
            // Restart with the same buffer (don't reset it)
            installTap()
            audioEngine.prepare()
            try audioEngine.start()
            // isRecording stays true
            print("✅ Recording resumed after \(reason)")
        } catch {
            isRecording = false
            errorMessage = "Recording stopped after \(reason): \(error.localizedDescription)"
            print("❌ Failed to resume recording: \(error)")
        }
    }
}

