import Foundation
import AVFoundation
import Combine

class VoiceManager: ObservableObject {
    static let shared = VoiceManager()
    
    @Published var availableEnglishVoices: [AVSpeechSynthesisVoice] = []
    @Published var availableTargetVoices: [AVSpeechSynthesisVoice] = []
    
    // UserDefaults Keys
    private let kSelectedEnglishVoice = "selectedEnglishVoiceIdentifier"
    private let kSelectedTargetVoice = "selectedTargetVoiceIdentifier"
    
    // Internal state to track current target language
    private var currentTargetLocaleID: String = "es-MX" // Default to Spanish (MX)
    
    init() {
        // Load initial target language from somewhere or default
        // For now, we load with default, but we'll expose a method to update it
        loadVoices()
        setupNotifications()
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func setupNotifications() {
        NotificationCenter.default.addObserver(forName: Notification.Name("AVSpeechSynthesisVoiceIdentifierDidChangeNotification"), object: nil, queue: .main) { [weak self] _ in
            self?.loadVoices()
        }
    }
    
    func updateTargetLanguage(to localeID: String) {
        guard !localeID.isEmpty else { return }
        self.currentTargetLocaleID = localeID
        loadVoices()
    }
    
    func loadVoices() {
        let allVoices = AVSpeechSynthesisVoice.speechVoices()
        
        // Blacklist of Novelty voices to exclude
        let noveltyVoices = Set([
            "Albert", "Bad News", "Bahh", "Bells", "Boing", "Bubbles", "Cellos", 
            "Deranged", "Good News", "Hysterical", "Junior", "Kathy", "Organ", 
            "Princess", "Ralph", "Trinoids", "Whisper", "Zarvox",
            "Jester", "Superstar", "Wobble", "Fred",
            "Eddy", "Flo", "Grandma", "Grandpa", "Reed", "Rocko", "Sandy", "Shelley"
        ])
        
        // Helper to sort by quality (Premium > Enhanced > Default)
        let sortComparator: (AVSpeechSynthesisVoice, AVSpeechSynthesisVoice) -> Bool = { v1, v2 in
            // First sort by Quality
            if v1.quality != v2.quality {
                // strict order: premium > enhanced > default
                let q1 = v1.quality == .premium ? 3 : (v1.quality == .enhanced ? 2 : 1)
                let q2 = v2.quality == .premium ? 3 : (v2.quality == .enhanced ? 2 : 1)
                return q1 > q2
            }
            // Then by Name
            return v1.name < v2.name
        }
        
        // Filter for English (US)
        availableEnglishVoices = allVoices.filter { 
            $0.language == "en-US" && !noveltyVoices.contains($0.name)
        }.sorted(by: sortComparator)
        
        // Filter for Target Language
        // Handle simplified codes (e.g. "fr" -> "fr-FR") if necessary, though usually we have full codes
        let targetVoices = allVoices.filter { 
            ($0.language == self.currentTargetLocaleID || $0.language.starts(with: self.currentTargetLocaleID)) && !noveltyVoices.contains($0.name)
        }
        
        if !targetVoices.isEmpty {
             availableTargetVoices = targetVoices.sorted(by: sortComparator)
        } else {
             // If no specific voices found, empty list (UI should handle fallback to system default)
             availableTargetVoices = []
        }
    }
    
    // MARK: - Selection Handling
    
    var selectedEnglishVoiceIdentifier: String {
        get {
            if let saved = UserDefaults.standard.string(forKey: kSelectedEnglishVoice) {
                return saved
            }
            // Default to Samantha if available
            if let samantha = availableEnglishVoices.first(where: { $0.name == "Samantha" }) {
                return samantha.identifier
            }
            return availableEnglishVoices.first?.identifier ?? AVSpeechSynthesisVoice(language: "en-US")?.identifier ?? ""
        }
        set {
            UserDefaults.standard.set(newValue, forKey: kSelectedEnglishVoice)
            objectWillChange.send()
        }
    }
    
    // We persist the selected voice PER language ideally, but for simplicity/user request 
    // we might just store one "Target Voice" preference. 
    // HOWEVER, a French voice ID won't work for Spanish.
    // So we should probably key the storage by the language code?
    // User plan didn't specify, but "Smart" behavior is better.
    // Let's use a dynamic key: "selectedVoice_\(currentTargetLocaleID)"
    
    var selectedTargetVoiceIdentifier: String {
        get {
            let key = "selectedVoice_\(currentTargetLocaleID)"
            if let saved = UserDefaults.standard.string(forKey: key) {
                // Verify this voice still exists and matches language (optional, but good)
                return saved
            }
            // Default logic: Premium/Enhanced if available
            return availableTargetVoices.first?.identifier ?? ""
        }
        set {
            let key = "selectedVoice_\(currentTargetLocaleID)"
            UserDefaults.standard.set(newValue, forKey: key)
            objectWillChange.send()
        }
    }
    
    func getSelectedEnglishVoice() -> AVSpeechSynthesisVoice? {
        if let voice = AVSpeechSynthesisVoice(identifier: selectedEnglishVoiceIdentifier) {
            return voice
        }
        return availableEnglishVoices.first ?? AVSpeechSynthesisVoice(language: "en-US")
    }
    
    func getSelectedTargetVoice() -> AVSpeechSynthesisVoice? {
        if let voice = AVSpeechSynthesisVoice(identifier: selectedTargetVoiceIdentifier) {
            return voice
        }
        // Fallback to any voice for this language
        return availableTargetVoices.first ?? AVSpeechSynthesisVoice(language: currentTargetLocaleID)
    }
    
    // MARK: - Descriptions
    
    func description(for voice: AVSpeechSynthesisVoice) -> String {
        var traits: [String] = []
        
        if voice.quality == .enhanced { traits.append("Enhanced") }
        if voice.quality == .premium { traits.append("Premium") }
        
        let qualitySuffix = traits.isEmpty ? "" : " (\(traits.joined(separator: ", ")))"
        
        // Detailed User Descriptions
        let knownDescriptions: [String: String] = [
            "Alex": "Male - Top Tier. The smartest voice; sounds very natural.",
            "Samantha": "Female - Standard. The classic 'original Siri' voice.",
            "Ava": "Female - Premium. Professional, warm, and very human-like.",
            "Allison": "Female - Premium. Lighter, breathy, and pleasant.",
            "Tom": "Male - Premium. Friendly, standard American male.",
            "Susan": "Female - Standard/Premium. Slightly formal and crisp.",
            "Zoe": "Female - Premium. Bright, cheerful, and younger-sounding.",
            "Evan": "Male - Enhanced. Deep, smooth, and modern.",
            "Nathan": "Male - Enhanced. Lighter, younger-sounding male.",
            "Noelle": "Female - Enhanced. Soft, sweet, and modern flow.",
            "Joelle": "Female - Enhanced. Clear, articulate, modern.",
            "Aaron": "Male - Neural. Standard American Male Siri voice.",
            "Nicky": "Female - Neural. Standard American Female Siri voice.",
            
            // Spanish
            "Paulina": "Female (MX) - Standard/Premium. Gold standard; professional news anchor style.",
            "Juan": "Male (MX) - Standard/Premium. Clear, neutral, polite.",
            "Siri Female": "Female (MX) - Neural. Very smooth, natural assistant.", 
            "Siri Male": "Male (MX) - Neural. Professional modern assistant.",
            "Monica": "Female (ES) - Clear Spanish (Spain).",
            "Jorge": "Male (ES) - Clear Spanish (Spain)."
        ]
        
        // Check exact name match first
        if let specificDesc = knownDescriptions[voice.name] {
            return "\(voice.name) - \(specificDesc)\(qualitySuffix)"
        }
        
        return "\(voice.name)\(qualitySuffix)"
    }
}
