import Foundation
import UIKit

class GeminiChatService: ObservableObject {
    static let shared = GeminiChatService()
    
    private let apiKey = "AIzaSyBUK6J5FjHta2nM0ZDCzuXc3HNURA8eXTU"
    private let apiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
    
    @Published var chatHistory: [ChatHistoryItem] = []
    
    private init() {}
    
    // MARK: - Chat History Management
    func clearChatHistory() {
        chatHistory.removeAll()
        print("🧹 Chat history cleared")
    }
    
    // MARK: - Main Message Sending
    func testConnection() async {
        // Test basic internet connectivity first
        let googleURL = URL(string: "https://www.google.com")!
        
        do {
            let (_, response) = try await URLSession.shared.data(from: googleURL)
        } catch {
            return
        }
        
        let testURL = URL(string: "https://generativelanguage.googleapis.com/v1beta/models?key=\(apiKey)")!
        
        do {
            let (data, response) = try await URLSession.shared.data(from: testURL)
        } catch {
            // Connection test failed
        }
        
        // Now test a simple generation request (like web app would do)
        await testSimpleGeneration()
    }
    
    func testSimpleGeneration() async {
        let url = URL(string: "\(apiURL)?key=\(apiKey)")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 30
        
        // Create a very simple request (like a web app would)
        let simplePayload = """
        {
            "contents": [
                {
                    "role": "user",
                    "parts": [{"text": "Say hello"}]
                }
            ],
            "generationConfig": {
                "responseMimeType": "text/plain",
                "temperature": 0.7,
                "maxOutputTokens": 100
            }
        }
        """
        
        request.httpBody = simplePayload.data(using: .utf8)
        
        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            
            if let httpResponse = response as? HTTPURLResponse {
                // Test completed successfully
            }
        } catch {
            // Test failed
        }
    }
    
    // MARK: - Creative Genre Suggestions
    func generateCreativeGenreSuggestions(basedOn songContext: String? = nil, currentGenres: [String] = [], selectedGenres: [String] = []) async throws -> [String] {
        // Build prompt for creative genre suggestions
        var prompt: String
        
        // If user has selected genres, suggest related/sub-genres
        if !selectedGenres.isEmpty {
            let lastSelectedGenre = selectedGenres.last!
            prompt = "Generate 8 related sub-genres and variations for '\(lastSelectedGenre)' music. "
            
            // Add context-specific suggestions based on the selected genre
            switch lastSelectedGenre.lowercased() {
            case "hip-hop", "hip hop":
                prompt += "Include variations like boom bap, trap, conscious rap, drill, old school, east coast, west coast, etc."
            case "rock":
                prompt += "Include variations like indie rock, punk rock, alternative rock, prog rock, classic rock, garage rock, etc."
            case "pop":
                prompt += "Include variations like indie pop, synth pop, dream pop, electropop, k-pop, art pop, etc."
            case "electronic":
                prompt += "Include variations like house, techno, ambient, drum & bass, trance, dubstep, etc."
            case "r&b", "rnb":
                prompt += "Include variations like neo soul, contemporary r&b, alternative r&b, funk, motown, etc."
            default:
                prompt += "Include specific sub-genres, regional variations, and closely related styles."
            }
        } else {
            // Initial broad genre suggestions
            prompt = "Generate 8 creative and diverse music genre suggestions for a user who wants to change the genre of their song."
            
            if let context = songContext, !context.isEmpty {
                prompt += " The current song is about: \(context)."
            }
            
            if !currentGenres.isEmpty {
                prompt += " Current genres are: \(currentGenres.joined(separator: ", "))."
            }
            
            prompt += " Include a mix of mainstream genres (pop, rock, hip-hop) and creative/unique genres (synthwave, lo-fi, afrobeat, indie folk, etc.)."
        }
        
        prompt += " Format each genre with a '+' prefix like '+boom bap', '+synthwave'. Respond with ONLY a comma-separated list of 8 genres with + prefixes, no explanations or extra text."
        
        let url = URL(string: "\(apiURL)?key=\(apiKey)")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 15
        
        let payload = """
        {
            "contents": [
                {
                    "role": "user",
                    "parts": [{"text": "\(prompt)"}]
                }
            ],
            "generationConfig": {
                "responseMimeType": "text/plain",
                "temperature": 0.9,
                "maxOutputTokens": 100
            }
        }
        """
        
        request.httpBody = payload.data(using: .utf8)
        
        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            
            guard let httpResponse = response as? HTTPURLResponse,
                  httpResponse.statusCode == 200 else {
                throw NSError(domain: "GeminiError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to get genre suggestions"])
            }
            
            let responseString = String(data: data, encoding: .utf8) ?? ""
            
            // Parse the response
            if let jsonData = responseString.data(using: .utf8),
               let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
               let candidates = json["candidates"] as? [[String: Any]],
               let firstCandidate = candidates.first,
               let content = firstCandidate["content"] as? [String: Any],
               let parts = content["parts"] as? [[String: Any]],
               let firstPart = parts.first,
               let text = firstPart["text"] as? String {
                
                // Parse comma-separated genres and add + prefix if not present
                let genres = text.trimmingCharacters(in: .whitespacesAndNewlines)
                    .components(separatedBy: ",")
                    .map { genre in
                        let trimmed = genre.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
                        let cleanGenre = trimmed.hasPrefix("+") ? String(trimmed.dropFirst()) : trimmed
                        return "+\(cleanGenre)"
                    }
                    .filter { !$0.isEmpty && !selectedGenres.contains($0) }
                    .prefix(8)
                
                return Array(genres)
            }
            
            throw NSError(domain: "GeminiError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to parse genre suggestions"])
            
        } catch {
            // Fallback to static creative suggestions if API fails with + prefix
            let fallbackGenres: [String]
            
            // Provide contextual fallbacks based on selected genres
            if !selectedGenres.isEmpty {
                let lastSelectedGenre = selectedGenres.last!.lowercased()
                
                switch lastSelectedGenre {
                case "hip-hop", "hip hop":
                    fallbackGenres = ["+boom bap", "+trap", "+conscious rap", "+drill", "+old school", "+east coast", "+west coast", "+chicago rap"]
                case "rock":
                    fallbackGenres = ["+indie rock", "+punk rock", "+alt rock", "+prog rock", "+classic rock", "+garage rock", "+post rock", "+math rock"]
                case "pop":
                    fallbackGenres = ["+indie pop", "+synth pop", "+dream pop", "+electropop", "+k-pop", "+art pop", "+bedroom pop", "+hyperpop"]
                case "electronic":
                    fallbackGenres = ["+house", "+techno", "+ambient", "+drum & bass", "+trance", "+dubstep", "+future bass", "+breakbeat"]
                case "r&b", "rnb":
                    fallbackGenres = ["+neo soul", "+contemporary r&b", "+alt r&b", "+funk", "+motown", "+quiet storm", "+new jack swing", "+smooth jazz"]
                default:
                    fallbackGenres = ["+indie", "+alternative", "+experimental", "+fusion", "+neo", "+contemporary", "+underground", "+crossover"]
                }
            } else {
                // General fallback genres
                fallbackGenres = [
                    "+synthwave", "+lo-fi", "+indie folk", "+dream pop", "+tropical house",
                    "+garage rock", "+neo soul", "+chill hop", "+dark wave", "+math rock",
                    "+future bass", "+bedroom pop", "+post punk", "+trip hop", "+shoegaze"
                ]
            }
            
            return Array(fallbackGenres.filter { !selectedGenres.contains($0) }.shuffled().prefix(8))
        }
    }
    
    // MARK: - Extend Request Handling
    private func isExtendRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        return lowercased.contains("extend") && lowercased.contains("after")
    }
    
    private func generateExtendResponse(_ message: String) -> String {
        // Extract song title and timestamp from the message
        // Format: "Extend [Song Title] after [timestamp]"
        
        var songTitle = "the song"
        var timestamp = "that point"
        
        // Try to extract song title (everything between "Extend " and " after")
        if let extendRange = message.range(of: "Extend ", options: .caseInsensitive),
           let afterRange = message.range(of: " after", options: .caseInsensitive) {
            let titleStartIndex = extendRange.upperBound
            let titleEndIndex = afterRange.lowerBound
            if titleStartIndex < titleEndIndex {
                songTitle = String(message[titleStartIndex..<titleEndIndex])
            }
        }
        
        // Try to extract timestamp (everything after "after ")
        if let afterRange = message.range(of: "after ", options: .caseInsensitive) {
            let timestampStartIndex = afterRange.upperBound
            let timestampString = String(message[timestampStartIndex...]).trimmingCharacters(in: .whitespacesAndNewlines)
            if !timestampString.isEmpty {
                timestamp = timestampString
            }
        }
        
        return "Ok got it, I'll extend \(songTitle) starting from \(timestamp). Before I proceed, let me know if you want to add lyrics to your extension or the vibe of your instrumental."
    }
    
    // MARK: - Lyrics Editing Request Handling
    private func isLyricsEditingRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        return lowercased.contains("create a new version") && lowercased.contains("with the following lyrics:")
    }
    
    private func generateLyricsEditingResponse(_ message: String) -> String {
        // Extract song title from the message
        // Format: "Can you create a new version of [Song Title] with the following lyrics:\n\n[lyrics]"
        
        var songTitle = "the song"
        var editedLyrics = ""
        
        // Try to extract song title (everything between "new version of " and " with the following lyrics")
        if let versionRange = message.range(of: "new version of ", options: .caseInsensitive),
           let lyricsRange = message.range(of: " with the following lyrics:", options: .caseInsensitive) {
            let titleStartIndex = versionRange.upperBound
            let titleEndIndex = lyricsRange.lowerBound
            if titleStartIndex < titleEndIndex {
                songTitle = String(message[titleStartIndex..<titleEndIndex])
            }
        }
        
        // Try to extract edited lyrics (everything after "following lyrics:\n\n")
        if let lyricsRange = message.range(of: "following lyrics:", options: .caseInsensitive) {
            let lyricsStartIndex = lyricsRange.upperBound
            let lyricsString = String(message[lyricsStartIndex...]).trimmingCharacters(in: .whitespacesAndNewlines)
            if !lyricsString.isEmpty {
                editedLyrics = lyricsString
            }
        }
        
        // Parse the edited lyrics into structured format
        let parsedLyrics = parseLyricsFromText(editedLyrics)
        
        // Create edited version of the original song title
        let baseTitleForEdited = extractBaseTitleFromVersionedTitle(songTitle)
        let editedTitle = "\(baseTitleForEdited) Edited"
        
        // Create a song response with the edited lyrics and "Edited" title
        let songResponse = SongResponse(
            reply: "I've updated the lyrics and created two new versions of \(songTitle)!",
            lyrics: parsedLyrics,
            songs: [
                Song(title: "\(editedTitle) (#1)", genres: ["pop", "indie"], artwork: nil, audioURL: "song1"),
                Song(title: "\(editedTitle) (#2)", genres: ["pop", "indie"], artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Change genre", "Edit lyrics", "Extend", "Replace section"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your lyrics edit request."
        } catch {
            return "Sorry, I couldn't process your lyrics edit request."
        }
    }
    
    private func parseLyricsFromText(_ text: String) -> LyricsStructure {
        var sections: [LyricsSection] = []
        
        // Split text by common section markers
        let lines = text.components(separatedBy: .newlines)
        var currentSection: String?
        var currentContent: [String] = []
        
        for line in lines {
            let trimmedLine = line.trimmingCharacters(in: .whitespaces)
            
            // Check if line is a section marker (like "[Chorus]", "[Verse 1]")
            if trimmedLine.hasPrefix("[") && trimmedLine.hasSuffix("]") {
                // Save previous section if it exists
                if let sectionType = currentSection, !currentContent.isEmpty {
                    sections.append(LyricsSection(
                        type: sectionType,
                        content: currentContent.joined(separator: "\n")
                    ))
                }
                
                // Start new section
                let sectionName = String(trimmedLine.dropFirst().dropLast())
                currentSection = sectionName
                currentContent = []
            } else if !trimmedLine.isEmpty {
                // Add content to current section
                currentContent.append(trimmedLine)
            }
        }
        
        // Add the last section
        if let sectionType = currentSection, !currentContent.isEmpty {
            sections.append(LyricsSection(
                type: sectionType,
                content: currentContent.joined(separator: "\n")
            ))
        }
        
        // If no sections were found, treat the whole text as a single section
        if sections.isEmpty && !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            sections.append(LyricsSection(
                type: "Lyrics",
                content: text.trimmingCharacters(in: .whitespacesAndNewlines)
            ))
        }
        
        return LyricsStructure(sections: sections)
    }
    
    private func extractBaseTitleFromVersionedTitle(_ title: String) -> String {
        // Extract base title from versioned title like "Song Title (#1)" -> "Song Title"
        if let range = title.range(of: " (#", options: .backwards) {
            return String(title[..<range.lowerBound])
        }
        return title
    }
    
    private func getNextVersionNumber() -> Int {
        // This would need to track version numbers properly in a real implementation
        // For now, return a placeholder
        return 3
    }
    
    // MARK: - Genre Change Request Handling
    private func isGenreChangeRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        return lowercased.contains("- change the genre to") || lowercased.contains("change the genre to")
    }
    
    private func generateGenreChangeResponse(_ message: String) -> String {
        // Extract genres and song title from the message
        // Format: "[Song Title (#1)] - Change the genre to [genre1, genre2, ...]"
        
        var genres: [String] = []
        var songTitle = "the song"
        
        // Try to extract song title (everything before " - Change the genre to")
        if let dashRange = message.range(of: " - change the genre to", options: .caseInsensitive) {
            songTitle = String(message[..<dashRange.lowerBound]).trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        // Try to extract genres (everything after "change the genre to ")
        if let genreRange = message.range(of: "change the genre to ", options: .caseInsensitive) {
            let genresStartIndex = genreRange.upperBound
            let genresString = String(message[genresStartIndex...]).trimmingCharacters(in: .whitespacesAndNewlines)
            
            // Parse comma-separated genres
            genres = genresString.components(separatedBy: ",").map { genre in
                genre.trimmingCharacters(in: .whitespacesAndNewlines)
            }.filter { !$0.isEmpty }
        }
        
        // If no song title was extracted from the message, try to get from context
        if songTitle == "the song" {
            songTitle = getCurrentRemixBaseTitleFromContext()
        }
        
        // Create a song response with remix titles
        let baseTitleForRemix = extractBaseTitleFromVersionedTitle(songTitle)
        let remixTitle = "\(baseTitleForRemix) Remix"
        
        let songResponse = SongResponse(
            reply: "I've remixed \(songTitle) in \(genres.joined(separator: " and ")) style! Here are two versions:",
            lyrics: nil, // No new lyrics for genre change, keep original
            songs: [
                Song(title: "\(remixTitle) (#1)", genres: genres, artwork: nil, audioURL: "song1"),
                Song(title: "\(remixTitle) (#2)", genres: genres, artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Change genre", "Edit lyrics", "Extend", "Replace section"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your genre change request."
        } catch {
            return "Sorry, I couldn't process your genre change request."
        }
    }
    
    private func getCurrentRemixBaseTitleFromContext() -> String {
        // Try to find the most recent song title from chat history
        for historyItem in chatHistory.reversed() {
            if historyItem.role == "model" {
                // Check if this message contains song data by trying to parse it as JSON
                let content = historyItem.parts.first?.text ?? ""
                if let data = content.data(using: .utf8),
                   let songResponse = try? JSONDecoder().decode(SongResponse.self, from: data),
                   let firstSong = songResponse.songs.first {
                    return firstSong.title
                }
            }
        }
        
        // Fallback if no song found in history
        return "Song Title"
    }
    
    // MARK: - Instrumental Extension Request Handling
    private func isInstrumentalExtensionRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        // Check if message contains instrumental-related keywords
        let instrumentalKeywords = ["instrumental", "no lyrics", "just music", "music only", "without lyrics", "no words", "beat only", "track only"]
        return instrumentalKeywords.contains { lowercased.contains($0) }
    }
    
    private func generateInstrumentalExtensionResponse(_ message: String) -> String {
        // Try to get the current song title from context
        let currentSong = getCurrentRemixBaseTitleFromContext()
        let baseTitleForExtension = extractBaseTitleFromVersionedTitle(currentSong)
        let extendedTitle = "\(baseTitleForExtension) Extended"
        
        // Create a song response with extended instrumental versions (no lyrics)
        let songResponse = SongResponse(
            reply: "Here's your extended version, I've created two versions for you to listen to",
            lyrics: nil, // No lyrics for instrumental
            songs: [
                Song(title: "\(extendedTitle) (#1)", genres: ["pop", "indie"], artwork: nil, audioURL: "song1"),
                Song(title: "\(extendedTitle) (#2)", genres: ["pop", "indie"], artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Change genre", "Edit lyrics", "Extend", "Replace section"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your instrumental extension request."
        } catch {
            return "Sorry, I couldn't process your instrumental extension request."
        }
    }
    
    // MARK: - Lyrics Extension Request Handling
    private func isLyricsExtensionRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        
        // First check if this is explicitly instrumental
        if isInstrumentalExtensionRequest(message) {
            return false
        }
        
        // Check if message contains lyrics-related keywords
        let lyricsKeywords = ["add lyrics", "with lyrics", "lyrics extension", "extend with lyrics", "more verses", "additional lyrics", "continue the song", "add more lyrics"]
        let hasLyricsKeyword = lyricsKeywords.contains { lowercased.contains($0) }
        
        // Check if message contains actual lyrics content (multiple lines, verse/chorus patterns)
        let hasMultipleLines = message.components(separatedBy: "\n").count > 2
        let hasLyricsStructure = lowercased.contains("[verse") || lowercased.contains("[chorus") || lowercased.contains("[bridge") || lowercased.contains("verse:") || lowercased.contains("chorus:")
        
        // Check for general extension context (following an extend request)
        let isGeneralExtension = lowercased.contains("extend") || 
            lowercased.contains("continue") || 
            lowercased.contains("add more") ||
            lowercased.contains("longer")
        
        // If it has lyrics content structure or keywords, treat as lyrics extension
        return hasLyricsKeyword || hasLyricsStructure || (hasMultipleLines && !isInstrumentalExtensionRequest(message)) || isGeneralExtension
    }
    
    private func generateLyricsExtensionResponse(_ message: String) -> String {
        // Try to get the current song title and lyrics from context
        let currentSong = getCurrentRemixBaseTitleFromContext()
        let baseTitleForExtension = extractBaseTitleFromVersionedTitle(currentSong)
        let extendedTitle = "\(baseTitleForExtension) Extended"
        
        // Check if user provided their own lyrics in the message
        let originalLyrics = getCurrentLyricsFromContext()
        let extendedLyrics: LyricsStructure
        
        if isUserProvidedLyrics(message) {
            // Use user's lyrics combined with original
            extendedLyrics = combineOriginalWithUserLyrics(originalLyrics, userLyrics: message)
        } else {
            // Generate extension lyrics automatically
            extendedLyrics = createExtendedLyrics(originalLyrics)
        }
        
        // Create a song response with extended lyrical versions
        let songResponse = SongResponse(
            reply: "Here's your extended version with the new lyrics, I've created two versions for you to listen to",
            lyrics: extendedLyrics,
            songs: [
                Song(title: "\(extendedTitle) (#1)", genres: ["pop", "indie"], artwork: nil, audioURL: "song1"),
                Song(title: "\(extendedTitle) (#2)", genres: ["pop", "indie"], artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Change genre", "Edit lyrics", "Extend", "Replace section"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your lyrics extension request."
        } catch {
            return "Sorry, I couldn't process your lyrics extension request."
        }
    }
    
    private func getCurrentLyricsFromContext() -> LyricsStructure? {
        // Try to find the most recent lyrics from chat history
        for historyItem in chatHistory.reversed() {
            if historyItem.role == "model" {
                let content = historyItem.parts.first?.text ?? ""
                if let data = content.data(using: .utf8),
                   let songResponse = try? JSONDecoder().decode(SongResponse.self, from: data),
                   let lyrics = songResponse.lyrics {
                    return lyrics
                }
            }
        }
        return nil
    }
    
    private func createExtendedLyrics(_ originalLyrics: LyricsStructure?) -> LyricsStructure {
        guard let originalLyrics = originalLyrics else {
            // Create new lyrics if none exist
            return LyricsStructure(sections: [
                LyricsSection(type: "Verse", content: "Taking this journey further down the road\nNew melodies and stories to unfold\nBuilding on the foundation that we've laid\nWith every note, new memories are made")
            ])
        }
        
        // Extend existing lyrics with additional sections
        var extendedSections = originalLyrics.sections
        
        // Add a new verse or bridge to extend the song
        let extensionSection = LyricsSection(
            type: "Bridge",
            content: "Now we're moving to a higher place\nFeeling the rhythm, picking up the pace\nThis extended journey takes us far\nShining bright like a distant star"
        )
        
        extendedSections.append(extensionSection)
        
        return LyricsStructure(sections: extendedSections)
    }
    
    private func isUserProvidedLyrics(_ message: String) -> Bool {
        // Check if message contains actual lyrics content
        let hasMultipleLines = message.components(separatedBy: "\n").count > 2
        let hasLyricsStructure = message.lowercased().contains("[verse") || 
                               message.lowercased().contains("[chorus") || 
                               message.lowercased().contains("[bridge") || 
                               message.lowercased().contains("verse:") || 
                               message.lowercased().contains("chorus:")
        
        // Look for poetic/lyrical patterns (rhyming lines, verse structure)
        let lines = message.components(separatedBy: "\n").filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
        let hasPoetryPattern = lines.count >= 2 && lines.allSatisfy { $0.split(separator: " ").count > 2 }
        
        return hasLyricsStructure || (hasMultipleLines && hasPoetryPattern)
    }
    
    private func combineOriginalWithUserLyrics(_ originalLyrics: LyricsStructure?, userLyrics: String) -> LyricsStructure {
        // Parse user's lyrics
        let userLyricsStructure = parseLyricsFromText(userLyrics)
        
        guard let originalLyrics = originalLyrics else {
            // If no original lyrics, just use user's lyrics
            return userLyricsStructure
        }
        
        // Combine original lyrics with user's extension
        var combinedSections = originalLyrics.sections
        combinedSections.append(contentsOf: userLyricsStructure.sections)
        
        return LyricsStructure(sections: combinedSections)
    }
    
    // MARK: - Specific Section Extension Request Handling
    private func isSpecificSectionRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        
        // Check for specific section requests
        let sectionKeywords = [
            "3rd verse", "third verse", "add verse", "new verse", "verse 3",
            "bridge", "add bridge", "new bridge",
            "chorus", "add chorus", "new chorus",
            "outro", "add outro", "ending",
            "pre-chorus", "add pre-chorus"
        ]
        
        return sectionKeywords.contains { lowercased.contains($0) }
    }
    
    private func generateSpecificSectionResponse(_ message: String) -> String {
        // Get current song context
        let currentSong = getCurrentRemixBaseTitleFromContext()
        let baseTitleForExtension = extractBaseTitleFromVersionedTitle(currentSong)
        let extendedTitle = "\(baseTitleForExtension) Extended"
        
        // Determine what section to add based on the message
        let sectionType = determineSectionType(from: message)
        let originalLyrics = getCurrentLyricsFromContext()
        let extendedLyrics = addSpecificSection(to: originalLyrics, sectionType: sectionType)
        
        // Create a song response with the new section
        let songResponse = SongResponse(
            reply: "Perfect! I've added a \(sectionType.lowercased()) to \(currentSong). Here are two versions for you to listen to",
            lyrics: extendedLyrics,
            songs: [
                Song(title: "\(extendedTitle) (#1)", genres: ["pop", "indie"], artwork: nil, audioURL: "song1"),
                Song(title: "\(extendedTitle) (#2)", genres: ["pop", "indie"], artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Change genre", "Edit lyrics", "Extend", "Replace section"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your section request."
        } catch {
            return "Sorry, I couldn't process your section request."
        }
    }
    
    private func determineSectionType(from message: String) -> String {
        let lowercased = message.lowercased()
        
        if lowercased.contains("verse") || lowercased.contains("3rd") || lowercased.contains("third") {
            return "Verse 3"
        } else if lowercased.contains("bridge") {
            return "Bridge"
        } else if lowercased.contains("chorus") {
            return "Chorus"
        } else if lowercased.contains("outro") || lowercased.contains("ending") {
            return "Outro"
        } else if lowercased.contains("pre-chorus") {
            return "Pre-Chorus"
        } else {
            return "Verse"
        }
    }
    
    private func addSpecificSection(to originalLyrics: LyricsStructure?, sectionType: String) -> LyricsStructure {
        // Get original lyrics or create empty structure
        var sections = originalLyrics?.sections ?? []
        
        // Generate new section content based on type
        let newSectionContent = generateSectionContent(for: sectionType)
        let newSection = LyricsSection(type: sectionType, content: newSectionContent)
        
        sections.append(newSection)
        
        return LyricsStructure(sections: sections)
    }
    
    private func generateSectionContent(for sectionType: String) -> String {
        switch sectionType.lowercased() {
        case "verse 3", "verse":
            return "Now the story takes a brand new turn\nLessons that we're here to learn\nEvery step along the way\nBrings us closer to today"
        case "bridge":
            return "And in this moment, we can see\nAll the possibilities\nRising up beyond the clouds\nSinging strong and singing loud"
        case "chorus":
            return "This is our time, this is our song\nWe've been waiting here so long\nEvery dream within our hearts\nThis is where our journey starts"
        case "outro":
            return "As the final notes ring true\nAll these memories stay with you\nUntil we meet again someday\nLet the music light your way"
        case "pre-chorus":
            return "Building up to something more\nEvery beat our hearts adore\nFeel the rhythm, feel the flow\nLet the melody just grow"
        default:
            return "Moving forward with the beat\nEvery moment feels complete\nIn the music we have found\nAll the magic all around"
        }
    }

    // MARK: - Custom Song Creation Request Handling
    private func isCustomSongCreationRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        
        // Check for various custom song creation formats
        let customSongPatterns = [
            "can you create custom a song:",
            "create custom song:",
            "custom song:",
            "make custom song:",
            "generate custom song:"
        ]
        
        let hasCustomPattern = customSongPatterns.contains { pattern in
            lowercased.contains(pattern)
        }
        
        // Must have custom pattern AND contain lyrics or style specification
        return hasCustomPattern && (lowercased.contains("lyrics:") || lowercased.contains("style:") || lowercased.contains("genre:"))
    }
    
    private func generateCustomSongResponse(_ message: String) -> String {
        // Parse the custom song creation message
        var lyrics = ""
        var style = ""
        var genre = ""
        var modelVersion = "v4.5"
        
        // Extract lyrics
        if let lyricsRange = message.range(of: "Lyrics:", options: .caseInsensitive) {
            let lyricsStartIndex = lyricsRange.upperBound
            
            // Find the end of lyrics (next section or end of string)
            var lyricsEndIndex = message.endIndex
            let nextSections = ["Style:", "Genre:", "Model Version:"]
            for section in nextSections {
                if let sectionRange = message.range(of: section, options: .caseInsensitive, range: lyricsStartIndex..<message.endIndex) {
                    lyricsEndIndex = min(lyricsEndIndex, sectionRange.lowerBound)
                }
            }
            
            lyrics = String(message[lyricsStartIndex..<lyricsEndIndex]).trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        // Extract style
        if let styleRange = message.range(of: "Style:", options: .caseInsensitive) {
            let styleStartIndex = styleRange.upperBound
            
            // Find the end of style (next section or end of string)
            var styleEndIndex = message.endIndex
            let nextSections = ["Genre:", "Model Version:"]
            for section in nextSections {
                if let sectionRange = message.range(of: section, options: .caseInsensitive, range: styleStartIndex..<message.endIndex) {
                    styleEndIndex = min(styleEndIndex, sectionRange.lowerBound)
                }
            }
            
            style = String(message[styleStartIndex..<styleEndIndex]).trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        // Extract genre
        if let genreRange = message.range(of: "Genre:", options: .caseInsensitive) {
            let genreStartIndex = genreRange.upperBound
            
            // Find the end of genre (next section or end of string)
            var genreEndIndex = message.endIndex
            if let modelRange = message.range(of: "Model Version:", options: .caseInsensitive, range: genreStartIndex..<message.endIndex) {
                genreEndIndex = modelRange.lowerBound
            }
            
            genre = String(message[genreStartIndex..<genreEndIndex]).trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        // Extract model version
        if let modelRange = message.range(of: "Model Version:", options: .caseInsensitive) {
            let modelStartIndex = modelRange.upperBound
            modelVersion = String(message[modelStartIndex...]).trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        // Generate appropriate song title based on content  
        let songTitle = generateSongTitleFromCustomContent(lyrics: lyrics, style: !style.isEmpty ? style : genre)
        
        // Parse lyrics into structured format if provided, otherwise leave null
        let parsedLyrics: LyricsStructure?
        if !lyrics.isEmpty {
            // Use the exact user-provided lyrics
            parsedLyrics = parseLyricsFromText(lyrics)
        } else {
            // If no lyrics provided, set to null (don't generate random lyrics)
            parsedLyrics = nil
        }
        
        // Determine genres: prioritize explicit Genre field, then extract from Style
        let genres: [String]
        if !genre.isEmpty {
            // Parse genres from explicit Genre field (could be comma-separated)
            genres = genre.components(separatedBy: ",")
                .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() }
                .filter { !$0.isEmpty }
        } else if !style.isEmpty {
            // Extract genres from style description as fallback
            genres = extractGenresFromStyle(style)
        } else {
            // Default genres
            genres = ["pop", "indie"]
        }
        
        // Create custom song response with appropriate messaging
        let replyMessage: String
        let styleOrGenre = !genre.isEmpty ? genre : style
        
        if !lyrics.isEmpty && !styleOrGenre.isEmpty {
            let genreText = !genre.isEmpty ? "genre" : "style"
            replyMessage = "Here you go! I've created your custom song with your lyrics and style. Here are two versions for you to listen to!"
        } else if !lyrics.isEmpty {
            replyMessage = "Perfect! I've created your custom song with your lyrics. Here are two versions for you to listen to!"
        } else if !styleOrGenre.isEmpty {
            let genreText = !genre.isEmpty ? "genre" : "style"
            replyMessage = "Perfect! I've created your custom song. Here are two versions for you to listen to!"
        } else {
            replyMessage = "Perfect! I've created your custom song. Here are two versions for you to listen to!"
        }
        
        let songResponse = SongResponse(
            reply: replyMessage,
            lyrics: parsedLyrics,
            songs: [
                Song(title: "\(songTitle) (#1)", genres: genres, artwork: nil, audioURL: "song1"),
                Song(title: "\(songTitle) (#2)", genres: genres, artwork: nil, audioURL: "song2")
            ],
            suggestions: ["Create more", "Edit lyrics", "Change genre", "Extend"]
        )
        
        // Convert to JSON string
        do {
            let encoder = JSONEncoder()
            let data = try encoder.encode(songResponse)
            return String(data: data, encoding: .utf8) ?? "Sorry, I couldn't process your custom song request."
        } catch {
            return "Sorry, I couldn't process your custom song request."
        }
    }
    
    private func generateSongTitleFromCustomContent(lyrics: String, style: String) -> String {
        // Try to extract theme from lyrics first
        if !lyrics.isEmpty {
            let words = lyrics.lowercased().components(separatedBy: .whitespacesAndNewlines)
                .filter { !$0.isEmpty && $0.count > 2 }
            
            // Look for meaningful words that could be titles
            let titleWords = ["love", "heart", "dream", "night", "light", "time", "world", "life", "home", "fire", "ocean", "star", "moon", "sun", "dance", "song", "music", "hope", "freedom", "journey", "magic", "wonder", "beautiful", "strong", "forever", "together", "memories", "story", "destiny", "adventure"]
            
            for word in titleWords {
                if words.contains(word) {
                    return word.capitalized
                }
            }
            
            // Fallback to first meaningful word
            if let meaningfulWord = words.first(where: { $0.count > 3 }) {
                return meaningfulWord.capitalized
            }
        }
        
        // Try to extract theme from style
        if !style.isEmpty {
            let styleWords = style.lowercased().components(separatedBy: .whitespacesAndNewlines)
                .filter { !$0.isEmpty && $0.count > 2 }
            
            if let meaningfulWord = styleWords.first(where: { $0.count > 3 && !["style", "genre", "music", "sound"].contains($0) }) {
                return meaningfulWord.capitalized
            }
        }
        
        // Default fallback titles
        let defaultTitles = ["Custom Song", "My Song", "New Track", "Original", "Creation"]
        return defaultTitles.randomElement() ?? "Custom Song"
    }
    
    private func extractGenresFromStyle(_ style: String) -> [String] {
        let lowercaseStyle = style.lowercased()
        var detectedGenres: [String] = []
        
        // Common genre keywords
        let genreMap: [String: String] = [
            "pop": "pop",
            "rock": "rock", 
            "hip-hop": "hip-hop",
            "hip hop": "hip-hop",
            "rap": "hip-hop",
            "r&b": "r&b",
            "rnb": "r&b",
            "indie": "indie",
            "electronic": "electronic",
            "edm": "electronic",
            "house": "house",
            "techno": "techno",
            "jazz": "jazz",
            "blues": "blues",
            "folk": "folk",
            "country": "country",
            "classical": "classical",
            "reggae": "reggae",
            "metal": "metal",
            "punk": "punk",
            "funk": "funk",
            "disco": "disco",
            "ambient": "ambient",
            "soul": "soul",
            "gospel": "gospel",
            "latin": "latin",
            "acoustic": "acoustic",
            "alternative": "alternative"
        ]
        
        for (keyword, genre) in genreMap {
            if lowercaseStyle.contains(keyword) {
                detectedGenres.append(genre)
            }
        }
        
        // Remove duplicates and limit to 3 genres
        detectedGenres = Array(Set(detectedGenres)).prefix(3).map { $0 }
        
        // Default genres if none detected
        if detectedGenres.isEmpty {
            detectedGenres = ["pop", "indie"]
        }
        
        return detectedGenres
    }
    
    // MARK: - Audio File Reference Request Handling
    private func isAudioFileReferenceRequest(_ message: String) -> Bool {
        let lowercased = message.lowercased()
        
        // Keywords that indicate user is referencing an attached audio file
        let audioReferencePatterns = [
            "make this into",
            "turn this into", 
            "remix this",
            "change this to",
            "convert this to",
            "make it more",
            "turn it into",
            "make this sound like",
            "can you make this",
            "transform this",
            "rework this"
        ]
        
        // Genre/style keywords that often follow audio references
        let styleKeywords = [
            "hip-hop", "hip hop", "rap", "rock", "pop", "jazz", "blues", "country", 
            "electronic", "techno", "house", "trap", "r&b", "rnb", "soul", "funk",
            "reggae", "folk", "classical", "metal", "punk", "indie", "alternative",
            "dance", "disco", "ambient", "chill", "lo-fi", "synthwave", "beat"
        ]
        
        let hasReferencePattern = audioReferencePatterns.contains { pattern in
            lowercased.contains(pattern)
        }
        
        let hasStyleKeyword = styleKeywords.contains { keyword in
            lowercased.contains(keyword)
        }
        
        return hasReferencePattern || (hasStyleKeyword && (lowercased.contains("this") || lowercased.contains("it")))
    }
    
    private func generateAudioFileReferenceResponse(_ message: String, audioFileName: String) -> String {
        // Extract the style/genre from the message
        let style = extractStyleFromAudioReferenceMessage(message)
        
        // Generate appropriate response based on the request
        let responses = [
            "Got you! I've remixed your audio. Here are two versions of the song:",
            "Perfect! I've transformed your track into \(style). Here are two versions:",
            "Nice! I've reworked your audio file. Here are two versions of the remix:",
            "Awesome! I've converted your track. Here are two versions of the song:"
        ]
        
        let baseResponse = responses.randomElement() ?? responses[0]
        
        // Create mock song response with the audio reference
        let songTitles = generateRemixTitles(style: style, originalFileName: audioFileName)
        
        let mockSongResponse = """
        {
            "reply": "\(baseResponse)",
            "songs": [
                {
                    "title": "\(songTitles[0])",
                    "genres": ["\(style.lowercased())", "remix"],
                    "audioURL": "song1"
                },
                {
                    "title": "\(songTitles[1])",
                    "genres": ["\(style.lowercased())", "remix"],
                    "audioURL": "song2"
                }
            ],
            "lyrics": {
                "sections": [
                    {
                        "type": "Verse",
                        "content": "Building on your sound\\nTaking it to new ground\\nEvery beat refined\\nYour vision redefined"
                    },
                    {
                        "type": "Chorus", 
                        "content": "This is your remix\\nBetter than before\\nThis is your remix\\nGiving you much more"
                    }
                ]
            },
            "suggestions": [
                "EXTEND",
                "EDIT LYRICS", 
                "REPLACE SECTION",
                "CHANGE GENRE",
                "MAKE SLOWER"
            ]
        }
        """
        
        print("🎵 Generated audio reference response JSON:")
        print(mockSongResponse)
        return mockSongResponse
    }
    
    private func extractStyleFromAudioReferenceMessage(_ message: String) -> String {
        let lowercased = message.lowercased()
        
        // Extract specific genres/styles mentioned
        let genreMap = [
            "hip-hop": "hip-hop", "hip hop": "hip-hop", "rap": "hip-hop",
            "rock": "rock", "pop": "pop", "jazz": "jazz", "blues": "blues",
            "country": "country", "electronic": "electronic", "techno": "techno",
            "house": "house", "trap": "trap", "r&b": "R&B", "rnb": "R&B",
            "soul": "soul", "funk": "funk", "reggae": "reggae", "folk": "folk",
            "classical": "classical", "metal": "metal", "punk": "punk",
            "indie": "indie", "alternative": "alternative", "dance": "dance",
            "disco": "disco", "ambient": "ambient", "chill": "chill",
            "lo-fi": "lo-fi", "synthwave": "synthwave", "beat": "beat"
        ]
        
        for (keyword, genre) in genreMap {
            if lowercased.contains(keyword) {
                return genre
            }
        }
        
        // Default to a generic style if none detected
        return "a new style"
    }
    
    private func generateRemixTitles(style: String, originalFileName: String) -> [String] {
        let baseName = originalFileName.replacingOccurrences(of: ".mp3", with: "").replacingOccurrences(of: ".wav", with: "")
        
        let titleTemplates = [
            "\(baseName) (\(style.capitalized) Remix)",
            "\(baseName) - \(style.capitalized) Version",
            "\(style.capitalized) \(baseName)",
            "\(baseName) (\(style.capitalized) Edit)"
        ]
        
        // Return two different variations
        let shuffled = titleTemplates.shuffled()
        return [shuffled[0], shuffled[1]]
    }

    // MARK: - Simple Text Response (for titles, etc.)
    func sendSimpleTextMessage(_ message: String) async throws -> String {
        let url = URL(string: "\(apiURL)?key=\(apiKey)")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 30
        
        let payload = """
        {
            "contents": [
                {
                    "role": "user",
                    "parts": [{"text": "\(message)"}]
                }
            ],
            "generationConfig": {
                "responseMimeType": "text/plain",
                "temperature": 0.7,
                "maxOutputTokens": 20
            }
        }
        """
        
        request.httpBody = payload.data(using: .utf8)
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NSError(domain: "GeminiError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])
        }
        
        guard httpResponse.statusCode == 200 else {
            throw NSError(domain: "GeminiError", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP Error: \(httpResponse.statusCode)"])
        }
        
        let responseString = String(data: data, encoding: .utf8) ?? ""
        
        // Parse the simple text response
        if let jsonData = responseString.data(using: .utf8),
           let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
           let candidates = json["candidates"] as? [[String: Any]],
           let firstCandidate = candidates.first,
           let content = firstCandidate["content"] as? [String: Any],
           let parts = content["parts"] as? [[String: Any]],
           let firstPart = parts.first,
           let text = firstPart["text"] as? String {
            return text.trimmingCharacters(in: .whitespacesAndNewlines)
        }
        
        throw NSError(domain: "GeminiError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to parse response"])
    }
    
    func sendMessage(
        _ userMessage: String,
        audioFileName: String? = nil,
        onStreamChunk: ((String) -> Void)? = nil
    ) async throws -> String {
        
        // Add user message to chat history
        let userChatItem = ChatHistoryItem(
            role: "user",
            parts: [ChatPart(text: userMessage)]
        )
        
        await MainActor.run {
            chatHistory.append(userChatItem)
        }
        
        // Check if this is a custom song creation request (check early to avoid conflicts)
        if isCustomSongCreationRequest(userMessage) {
            return generateCustomSongResponse(userMessage)
        }
        
        // Check if this is an audio file reference request
        if let audioFileName = audioFileName, isAudioFileReferenceRequest(userMessage) {
            return generateAudioFileReferenceResponse(userMessage, audioFileName: audioFileName)
        }
        
        // Check if this is an extend request
        if isExtendRequest(userMessage) {
            return generateExtendResponse(userMessage)
        }
        
        // Check if this is a lyrics editing request
        if isLyricsEditingRequest(userMessage) {
            return generateLyricsEditingResponse(userMessage)
        }
        
        // Check if this is a genre change request
        if isGenreChangeRequest(userMessage) {
            return generateGenreChangeResponse(userMessage)
        }
        
        // Check if this is an instrumental extension request
        if isInstrumentalExtensionRequest(userMessage) {
            return generateInstrumentalExtensionResponse(userMessage)
        }
        
        // Check if this is a specific section extension request (like "3rd verse", "bridge", etc.)
        if isSpecificSectionRequest(userMessage) {
            return generateSpecificSectionResponse(userMessage)
        }
        
        // Check if this is a lyrics extension request
        if isLyricsExtensionRequest(userMessage) {
            return generateLyricsExtensionResponse(userMessage)
        }
        
        // Check message type for response format
        let isDetailedEdit = checkForDetailedEdit(userMessage)
        let isReferenceEdit = checkForReferenceEdit(userMessage)
        
        
        // Get system context
        let systemContext = getSystemContext(isReferenceEdit: isReferenceEdit, isDetailedEdit: isDetailedEdit)
        
        // Create API payload
        let payload = createAPIPayload(
            systemContext: systemContext,
            isDetailedEdit: isDetailedEdit,
            isReferenceEdit: isReferenceEdit
        )
        
        // Try real API first, fallback to mock if network fails
        do {
            print("🌐 Attempting real API call...")
            let response = try await sendMessageFallback(payload: payload, isReferenceEdit: isReferenceEdit, isDetailedEdit: isDetailedEdit)
            print("✅ Real API success!")
            return response
        } catch {
            print("❌ Real API failed: \(error)")
            print("🚧 Falling back to mock data...")
            
            let mockResponse = await useMockResponse(for: userMessage)
            
            // Add to chat history for mock response
            let aiChatItem = ChatHistoryItem(
                role: "model",
                parts: [ChatPart(text: mockResponse)]
            )
            
            await MainActor.run {
                chatHistory.append(aiChatItem)
            }
            
            return mockResponse
        }
    }
    
    // MARK: - Mock Response (Temporary)
    private func useMockResponse(for userMessage: String) async -> String {
        // Simulate API delay
        try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
        
        let topic = userMessage.contains("cat") ? "your cat" : userMessage.contains("cafe") ? "cafes" : userMessage.contains("friend") ? "friendship" : "your topic"
        let title = userMessage.contains("cat") ? "Whiskers" : userMessage.contains("cafe") ? "Cafe Dreams" : userMessage.contains("friend") ? "Best Friend" : "My Song"
        
        // Build JSON using string concatenation to avoid interpolation issues
        let replyText = "Here you go, I made two versions of the song about \(topic). Let me know what you want to make edits, I can also create more versions."
        
        let mockSongResponse = "{" +
        "\"reply\": \"\(replyText)\"," +
        "\"lyrics\": {" +
            "\"sections\": [" +
                "{" +
                    "\"type\": \"Chorus\"," +
                    "\"content\": \"My little friend with whiskers bright\\nCurled up cozy through the night\\nPurring softly by my side\\nIn your love I can confide\"" +
                "}," +
                "{" +
                    "\"type\": \"Verse 1\"," +
                    "\"content\": \"Morning sunbeams find you there\\nStretching paws without a care\\nChasing shadows, chasing dreams\\nLife is better than it seems\"" +
                "}" +
            "]" +
        "}," +
        "\"songs\": [" +
            "{" +
                "\"title\": \"\(title) (#1)\"," +
                "\"genres\": [\"indie\", \"folk\"]," +
                "\"audioURL\": \"song1\"" +
            "}," +
            "{" +
                "\"title\": \"\(title) (#2)\"," +
                "\"genres\": [\"pop\", \"acoustic\"]," +
                "\"audioURL\": \"song2\"" +
            "}" +
        "]," +
        "\"suggestions\": [\"Create more\", \"Extend\", \"Make it faster\", \"Add a bridge\"]" +
        "}"
        
        
        return mockSongResponse
    }
    
    // MARK: - Streaming Implementation
    private func sendStreamingMessage(
        payload: GeminiRequest,
        onStreamChunk: ((String) -> Void)?
    ) async throws -> String {
        
        let url = URL(string: "\(apiURL)?alt=sse&key=\(apiKey)")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 60 // 60 second timeout
        request.httpBody = try JSONEncoder().encode(payload)
        
        let (asyncBytes, response) = try await URLSession.shared.bytes(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        
        var fullResponse = ""
        
        for try await line in asyncBytes.lines {
            if line.hasPrefix("data: ") {
                let data = String(line.dropFirst(6))
                if data.trimmingCharacters(in: .whitespacesAndNewlines) == "[DONE]" {
                    continue
                }
                
                if let jsonData = data.data(using: .utf8),
                   let streamData = try? JSONDecoder().decode(StreamingData.self, from: jsonData),
                   let candidate = streamData.candidates?.first,
                   let content = candidate.content,
                   let part = content.parts?.first,
                   let textChunk = part.text {
                    
                    fullResponse += textChunk
                    
                    // Call the stream chunk handler on main thread
                    await MainActor.run {
                        onStreamChunk?(fullResponse)
                    }
                }
            }
        }
        
        if fullResponse.isEmpty {
            throw URLError(.cannotParseResponse)
        }
        
        return fullResponse
    }
    
    // MARK: - Fallback Implementation
    private func sendMessageFallback(
        payload: GeminiRequest,
        isReferenceEdit: Bool,
        isDetailedEdit: Bool
    ) async throws -> String {
        
        let url = URL(string: "\(apiURL)?key=\(apiKey)")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.timeoutInterval = 60 // 60 second timeout
        request.httpBody = try JSONEncoder().encode(payload)
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        
        let geminiResponse = try JSONDecoder().decode(GeminiResponse.self, from: data)
        
        guard let candidate = geminiResponse.candidates?.first,
              let content = candidate.content,
              let part = content.parts?.first,
              let aiResponse = part.text else {
            throw URLError(.cannotParseResponse)
        }
        
        
        // Add to history if not already added
        let lastMessage = chatHistory.last
        if lastMessage?.role != "model" {
            let aiChatItem = ChatHistoryItem(
                role: "model",
                parts: [ChatPart(text: aiResponse)]
            )
            
            await MainActor.run {
                chatHistory.append(aiChatItem)
            }
        }
        
        // Generate artwork if needed
        if !isReferenceEdit && !isDetailedEdit {
            return try await addArtworkToResponse(aiResponse)
        }
        
        return aiResponse
    }
    
    // MARK: - Helper Methods
    private func checkForDetailedEdit(_ message: String) -> Bool {
        // Only detect detailed edits for very specific edit commands
        // Don't match general song creation requests
        let patterns = [
            "^extend$",
            "^edit lyrics$",
            "^change genre$",
            "^replace section$",
            "^edit speed$"
        ]
        
        let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines)
        
        // Don't classify as edit if it contains song creation keywords
        let songCreationKeywords = ["make a song", "create a song", "song about", "I want", "can you"]
        for keyword in songCreationKeywords {
            if trimmed.lowercased().contains(keyword) {
                return false
            }
        }
        
        for pattern in patterns {
            if trimmed.range(of: pattern, options: [.regularExpression, .caseInsensitive]) != nil {
                return true
            }
        }
        
        return false
    }
    
    private func checkForReferenceEdit(_ message: String) -> Bool {
        let hasContext = message.range(of: "\\[CONTEXT:\\s*User is editing \".*\"\\]", options: .regularExpression) != nil
        let hasIndicators = message.contains("- \"") || message.contains(": Lyrics") || 
                           message.contains("Chorus") || message.contains("Verse")
        
        return hasContext && hasIndicators
    }
    
    private func createAPIPayload(
        systemContext: String,
        isDetailedEdit: Bool,
        isReferenceEdit: Bool
    ) -> GeminiRequest {
        
        // Convert chat history to Gemini format
        var contents: [GeminiContent] = []
        
        // Add system context
        contents.append(GeminiContent(
            role: "user",
            parts: [GeminiPart(text: systemContext)]
        ))
        
        // Add chat history
        for item in chatHistory {
            contents.append(GeminiContent(
                role: item.role,
                parts: item.parts.map { GeminiPart(text: $0.text) }
            ))
        }
        
        let responseMimeType = (isDetailedEdit || isReferenceEdit) ? "text/plain" : "application/json"
        
        return GeminiRequest(
            contents: contents,
            generationConfig: GenerationConfig(
                responseMimeType: responseMimeType,
                temperature: 0.9,
                maxOutputTokens: 2048
            )
        )
    }
    
    private func addArtworkToResponse(_ response: String) async throws -> String {
        guard let data = response.data(using: .utf8),
              var songResponse = try? JSONDecoder().decode(SongResponse.self, from: data) else {
            print("Response is not JSON, skipping artwork generation")
            return response
        }
        
        print("Generating album artwork for songs...")
        
        // Generate simplified artwork for each song (prevent crashes)
        let updatedSongs = songResponse.songs.enumerated().map { index, song in
            // Simple gradient artwork instead of complex SVG
            let artwork = "gradient_\(index)"
            
            return Song(title: song.title, genres: song.genres, artwork: artwork, audioURL: song.audioURL)
        }
        
        // Create new SongResponse with updated songs
        songResponse = SongResponse(
            reply: songResponse.reply,
            lyrics: songResponse.lyrics,
            songs: updatedSongs,
            suggestions: songResponse.suggestions
        )
        
        // Convert back to JSON
        let encoder = JSONEncoder()
        let updatedData = try encoder.encode(songResponse)
        return String(data: updatedData, encoding: .utf8) ?? response
    }
    
    // MARK: - System Context
    private func getSystemContext(isReferenceEdit: Bool, isDetailedEdit: Bool) -> String {
        if isReferenceEdit {
            return """
            You are helping a user edit lyrics for a song. The user is editing a specific word or line of lyrics.

            The user's message contains context about what they're editing in the format [CONTEXT: ...].

            IMPORTANT: This is NOT song generation. You should ONLY provide lyric options, never generate full songs with JSON responses.

            Your job is to provide exactly 5 different replacement options for the word or line they selected:

            For WORD editing:
            - Provide 5 alternative words that fit the context
            - Keep the same rhythm and syllable count when possible
            - Make them contextually appropriate

            For LINE editing:
            - Provide 5 alternative lines that fit the verse/chorus structure
            - Keep similar rhythm and flow
            - Maintain the emotional tone or modify as requested

            Format your response as:
            Here are some options for you:

            1. first option
            2. second option  
            3. third option
            4. fourth option
            5. fifth option

            Which version do you like best?

            Do NOT:
            - Generate full songs or JSON responses
            - Add explanations or commentary beyond the friendly message
            - Create song structures
            - Include titles or metadata
            """
        } else {
            return """
            You make songs for Suno. At the moment you can support the following:
            - Create songs - New song, cover, remix, alternate version, change genre
            - Extend songs
            - Edit Lyrics
            - Edit Speed  
            - Change Genre
            - Replace Section

            IMPORTANT: Only generate full songs (JSON responses) when the user explicitly requests song creation with phrases like:
            - "create a song"
            - "make a song"
            - "generate a song"
            - "write a song"
            - "I want a song about..."
            - "create more versions"
            - "make more"

            If the user asks about something that's not song-related, engage them with follow-up questions to help them create a song. For example:
            - If they mention a mood/emotion: "That sounds interesting! Would you like me to create a song about [their topic]? What genre or style are you thinking?"
            - If they mention an activity/event: "I could help you create a song about [their topic]! What kind of vibe are you going for - upbeat, chill, emotional?"
            - If they just say "hello" or casual chat: "Hey! I'm here to help you create amazing songs. What's on your mind today? Any particular mood, story, or theme you'd like to turn into music?"

            Only say "Sorry can't do that yet, maybe you should try Custom Mode" for technical requests that are clearly outside of music creation.

            When the user explicitly requests song creation, generate original song lyrics and a creative song title based on the topic. 

            IMPORTANT: Both songs should have the SAME base title, with version numbers in parentheses: "(#1)" and "(#2)". For example: "Summer Nights (#1)" and "Summer Nights (#2)".

            TITLE LENGTH RULE: Keep song titles to a maximum of 2 words only. Examples: "Summer Nights", "Broken Dreams", "Wild Hearts", "City Life", "Midnight Dance".

            TITLE REUSE RULE: If the user is asking for "more versions", "create another", "make more", or similar requests without changing the core topic/prompt, keep using the SAME base title from the previous songs and continue the version numbering (e.g., "(#3)", "(#4)"). Only generate a completely NEW title when the user requests a song about a different topic or theme.

            LYRICS STRUCTURE RULE: All lyrics must follow this exact structure with 4 sections:
            - Chorus (4-6 lines of chorus lyrics)
            - Verse 1 (4-6 lines of verse 1 lyrics)  
            - Chorus (Repeat the exact same chorus)
            - Verse 2 (4-6 lines of verse 2 lyrics)

            You must respond with a JSON object containing:
            1. "reply" - A friendly message like "Here you go, I made two versions of the song. Let me know what you want to make edits."
            2. "lyrics" - A structured object with individual sections (ONLY include lyrics for entirely NEW songs. If the user is asking for "more versions", "create another", "make more", etc. of an existing song, set this to null since the lyrics haven't changed)
            3. "songs" - An array with 2 song objects, each containing "title" and "genres". Both versions should use the same lyrics - they represent different musical arrangements of the same song
            4. "suggestions" - An array of suggestion pills like ["Extend", "Edit Lyrics", "Change Genre", "Replace Section"]

            GENRES RULE: Each song should include "genres" as an array of 1-3 genre strings. If the user specifies genres, use those. If not, choose appropriate genres based on the song content and style. Examples: ["pop", "indie"], ["rock", "alternative", "grunge"], ["hip-hop", "r&b"]

            Format for NEW songs:
            {
              "reply": "",
              "lyrics": {
                "sections": [
                  {
                    "type": "chorus",
                    "content": "(chorus lyrics here)"
                  },
                  {
                    "type": "verse1",
                    "content": "(verse 1 lyrics here)"
                  },
                  {
                    "type": "chorus",
                    "content": "(same chorus lyrics as above)"
                  },
                  {
                    "type": "verse2", 
                    "content": "(verse 2 lyrics here)"
                  }
                ]
              },
              "songs": [
                {"title": "Song Title (#1)", "genres": ["pop", "indie"]},
                {"title": "Song Title (#2)", "genres": ["pop", "indie"]}
              ],
              "suggestions": ["Extend", "Edit Lyrics", "Change Genre", "Replace Section"]
            }

            Format for MORE VERSIONS of existing songs:
            {
              "reply": "",
              "lyrics": null,
              "songs": [
                {"title": "Song Title (#3)", "genres": ["pop", "indie"]},
                {"title": "Song Title (#4)", "genres": ["pop", "indie"]}
              ],
              "suggestions": ["Extend", "Edit Lyrics", "Change Genre", "Replace Section"]
            }

            When editing a song, look for [CONTEXT: ...] information in the user's message which will tell you exactly which song version they want to edit. 

            For CONVERSATIONAL EDITS (like "make the lyrics funnier", "extend by 30s", "make it more upbeat"):
            - Treat these as song creation requests that generate new versions
            - Use the same JSON format as regular song creation
            - Apply the requested changes to create new song versions
            - Reference the original song from context when creating new versions
            - Keep the same base title but increment version numbers

            For DETAILED EDITS when user types them (not from suggestion pills):
            - When users type "edit lyrics" in chat: Ask conversationally what they want to edit, like "What part of the lyrics do you want to edit?"
            - For other detailed edits (like "change genre", "replace section", "extend"): Respond with messages that include a clickable editor link:
              - "Change Genre": "I remixed this song in the style of your genres. Here are two versions."
              - "Replace Section": "Opening Song Editor..."
              - "Extend": "Opening Song Editor..."
            - This should be a plain text response, not JSON
            - The "Lyrics Editor" and "Song Editor" parts will become clickable links in the UI that open the sheet
            """
        }
    }
    
    // MARK: - Album Artwork Generation
    private func generateAlbumArtwork(songTitle: String, genres: [String], uniqueSeed: String) -> String {
        let genreColors: [String: [String]] = [
            "pop": ["#ff6b6b", "#4ecdc4"],
            "rock": ["#ff4757", "#2f3542"],
            "hip-hop": ["#a55eea", "#26de81"],
            "rnb": ["#fd79a8", "#00cec9"],
            "indie": ["#fd79a8", "#fdcb6e"],
            "electronic": ["#74b9ff", "#00b894"],
            "jazz": ["#fdcb6e", "#e17055"],
            "classical": ["#81ecec", "#fab1a0"],
            "country": ["#fdcb6e", "#00b894"],
            "alternative": ["#a29bfe", "#6c5ce7"]
        ]
        
        // Get colors based on the first genre, default to pop
        let primaryGenre = genres.first?.lowercased() ?? "pop"
        var colors = genreColors[primaryGenre] ?? genreColors["pop"]!
        
        // Create a unique seed combining title, timestamp, and unique seed
        let timestamp = Int(Date().timeIntervalSince1970)
        let uniqueString = "\(songTitle)_\(timestamp)_\(uniqueSeed)"
        
        // Hash the unique string to get varied angles and positions
        let hash = abs(uniqueString.hash)
        
        // Add color variation based on hash
        let colorVariation = hash % 4
        if colorVariation == 1 {
            // Swap colors
            colors = [colors[1], colors[0]]
        } else if colorVariation == 2 {
            // Use colors from a different genre
            let allColors = Array(genreColors.values.flatMap { $0 })
            if !allColors.isEmpty {
                colors = [colors[0], allColors[hash % allColors.count]]
            }
        } else if colorVariation == 3 {
            // Use colors from a related genre
            let genreKeys = Array(genreColors.keys)
            let altGenre = genreKeys[(hash * 3) % genreKeys.count]
            colors = genreColors[altGenre] ?? colors
        }
        
        let angle = hash % 360
        let opacity1 = 0.7 + Double(hash % 30) / 100.0
        let opacity2 = 0.5 + Double((hash * 2) % 40) / 100.0
        
        print("Generated gradient for \"\(songTitle)\": \(colors), angle: \(angle)deg")
        
        // Create SVG gradient
        let svg = """
        <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
          <defs>
            <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
              <stop offset="0%" style="stop-color:\(colors[0]);stop-opacity:\(opacity1)" />
              <stop offset="100%" style="stop-color:\(colors[1]);stop-opacity:\(opacity2)" />
            </linearGradient>
          </defs>
          <rect width="200" height="200" fill="url(#grad)" />
        </svg>
        """
        
        // Return data URI
        let base64 = Data(svg.utf8).base64EncodedString()
        return "data:image/svg+xml;base64,\(base64)"
    }
    
    // MARK: - History Management
    func clearHistory() {
        chatHistory.removeAll()
    }
    
    func getHistory() -> [ChatHistoryItem] {
        return chatHistory
    }
}
