import SwiftUI
import UIKit
import AVFoundation
#if canImport(Translation)
import Translation
#endif
import Combine
import GameController

struct NoteItem: Identifiable, Hashable, Codable {
    let id: UUID
    var content: String
    var timestamp: Date
    var group: String?
    var isHiddenFromHistory: Bool
    var displayName: String?
    
    init(content: String, timestamp: Date, group: String? = nil, isHiddenFromHistory: Bool = false, displayName: String? = nil) {
        self.id = UUID()
        self.content = content
        self.timestamp = timestamp
        self.group = group
        self.isHiddenFromHistory = isHiddenFromHistory
        self.displayName = displayName
    }
}

struct ContentView: View {
    @EnvironmentObject var themeManager: ThemeManager
    @State private var currentNote: String = ""
    @State private var savedNotes: [NoteItem] = []
    @State private var groups: [String] = []
    @State private var groupColors: [String: String] = [:] // Map group name to color name
    
    @State private var isMenuOpen: Bool = false
    @State private var isGroupsMenuOpen: Bool = false
    @State private var dragOffset: CGFloat = 0
    
    @State private var fontSize: CGFloat = 40
    @State private var keyboardHeight: CGFloat = 0
    @FocusState private var isInputActive: Bool
    
    // Group Selection State
    @State private var noteForGroupSelection: NoteItem?
    @State private var showGroupSelectionSheet: Bool = false
    @State private var showCreateGroupAlert: Bool = false
    @State private var newGroupName: String = ""
    
    // Group Rename State
    @State private var groupToRename: String?
    @State private var showRenameGroupAlert: Bool = false
    @State private var newGroupNameInput: String = ""
    
    // Rename State
    @State private var noteForRenaming: NoteItem?
    @State private var showRenameAlert: Bool = false
    @State private var newDisplayName: String = ""
    
    // Settings State
    @State private var showSettings: Bool = false
    
    // Voice Screen State
    @State private var isVoiceScreenActive: Bool = false
    
    // Split Screen State
    @State private var isSplitScreenMode: Bool = false
    @State private var splitScreenLayoutID = UUID() // For forcing layout updates
    @ObservedObject private var speechRecognizer = SpeechRecognizer.shared
    
    // Text-to-Speech
    @State private var speechSynthesizer = AVSpeechSynthesizer()
    @StateObject private var speechDelegate = SpeechDelegate()
    @State private var isSpeakingAnimation = false
    
    // Translation State
    @State private var isShowingTranslation = false
    @State private var translatedNote = ""
    @State private var translationTaskID = UUID()
    @ObservedObject private var languageManager = LanguageManager.shared
    @AppStorage("targetLanguageIdentifier") private var targetLanguageIdentifier: String = ""
    
    // Resume Speaking State
    @State private var lastSpokenText: String = ""
    @State private var currentUtteranceOffset: Int = 0
    @State private var isContinuousMode: Bool = false
    
    #if canImport(Translation)
    @State private var mainTranslationConfig: TranslationSession.Configuration?
    // Split screen translation configs
    @State private var splitTranscriptConfig: TranslationSession.Configuration?
    @State private var splitUserTextConfig: TranslationSession.Configuration?
    #endif
    
    // Split screen translation state
    @State private var translatedTranscript = ""  // Hearing person's text translated to English
    @State private var translatedUserText = ""    // User's text translated to target language
    
    // Hardware Keyboard State
    @State private var isHardwareKeyboardAttached: Bool = false
    
    var body: some View {
        GeometryReader { geometry in
            let menuWidth = geometry.size.width * 0.75
            // Calculate the actual height taken by the keyboard within the safe area
            let keyboardOverlap = max(0, keyboardHeight - geometry.safeAreaInsets.bottom)
            let availableHeight = geometry.size.height - keyboardOverlap
            
            ZStack(alignment: .leading) {
                if isVoiceScreenActive {
                    VoiceNoteView(onFlipBack: {
                        withAnimation(.easeInOut(duration: 0.6)) {
                            isVoiceScreenActive = false
                        }
                        // Ensure keyboard comes up when returning
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                            isInputActive = true
                        }
                    })
                    .transition(.modifier(
                        active: FlipTransition(angle: 90),
                        identity: FlipTransition(angle: 0)
                    ))
                } else {
                    // Main Content
                    mainContentView(geometry: geometry, keyboardOverlap: keyboardOverlap)
                }
                
                // Dimming overlay when any menu is open
                if isMenuOpen || isGroupsMenuOpen {
                    Color.black.opacity(0.3)
                        .ignoresSafeArea()
                        .onTapGesture {
                            withAnimation {
                                isMenuOpen = false
                                isGroupsMenuOpen = false
                            }
                        }
                }
                
                // Left Side Menu (History)
                SideMenuView(
                    notes: savedNotes.filter { !$0.isHiddenFromHistory },
                    onSelect: { selectedNote in
                        currentNote = selectedNote.content
                        withAnimation {
                            isMenuOpen = false
                            isVoiceScreenActive = false
                        }
                        // Recalculate font after loading
                        DispatchQueue.main.async {
                            adjustFontSize(containerSize: CGSize(width: geometry.size.width, height: availableHeight - 60))
                        }
                    },
                    onStarTap: { note in
                        noteForGroupSelection = note
                        if groups.isEmpty {
                            showCreateGroupAlert = true
                        } else {
                            showGroupSelectionSheet = true
                        }
                    },
                    onClearAll: {
                        clearAllHistory()
                    },
                    onDelete: { note in
                        deleteNoteFromHistory(note)
                    },
                    onSettings: {
                        showSettings = true
                    },
                    onClose: {
                        withAnimation {
                            isMenuOpen = false
                        }
                    }
                )
                .frame(width: menuWidth)
                .offset(x: isMenuOpen ? 0 : -menuWidth)
                .animation(.easeInOut, value: isMenuOpen)

                
                // Right Side Menu (Groups)
                GroupsMenuView(
                    groups: groups,
                    groupColors: groupColors,
                    notes: savedNotes,
                    getGroupColor: getGroupColor,
                    onSelect: { selectedNote in
                        currentNote = selectedNote.content
                        withAnimation {
                            isGroupsMenuOpen = false
                            isVoiceScreenActive = false
                        }
                        DispatchQueue.main.async {
                            adjustFontSize(containerSize: CGSize(width: geometry.size.width, height: availableHeight - 60))
                        }
                    },
                    onRenameNote: { note in
                        noteForRenaming = note
                        newDisplayName = note.displayName ?? note.content
                        showRenameAlert = true
                    },
                    onRenameGroup: { group in
                        groupToRename = group
                        newGroupNameInput = group
                        showRenameGroupAlert = true
                    },
                    onColorGroup: { group, color in
                        setGroupColor(group: group, color: color)
                    },
                    onDelete: { note in
                        deleteNote(note)
                    },
                    onDeleteGroup: { group in
                        deleteGroup(group)
                    },
                    onClose: {
                        withAnimation {
                            isGroupsMenuOpen = false
                        }
                    }
                )
                .frame(width: menuWidth)
                .offset(x: isGroupsMenuOpen ? geometry.size.width - menuWidth : geometry.size.width)
                .animation(.easeInOut, value: isGroupsMenuOpen)

                
                // Edge Swipe Gesture Areas
                
                // Left Edge (History)
                HStack {
                    Color.clear
                        .frame(width: 20)
                        .contentShape(Rectangle())
                        .allowsHitTesting(!isMenuOpen && !isGroupsMenuOpen)
                        .gesture(
                            DragGesture()
                                .onChanged { value in
                                    if value.translation.width > 0 {
                                        // Swipe Right
                                    }
                                }
                                .onEnded { value in
                                    if value.translation.width > 50 {
                                        saveCurrentNote()
                                        isInputActive = false // Hide keyboard
                                        withAnimation {
                                            isMenuOpen = true
                                        }
                                    }
                                }
                        )
                    Spacer()
                }
                
                // Right Edge (Groups)
                HStack {
                    Spacer()
                    Color.clear
                        .frame(width: 20)
                        .contentShape(Rectangle())
                        .allowsHitTesting(!isMenuOpen && !isGroupsMenuOpen)
                        .gesture(
                            DragGesture()
                                .onEnded { value in
                                    if value.translation.width < -50 {
                                        isInputActive = false // Hide keyboard
                                        withAnimation {
                                            isGroupsMenuOpen = true
                                        }
                                    }
                                }
                        )
                }
            }
            .ignoresSafeArea(.keyboard, edges: .bottom) // Prevent automatic resizing
            .onAppear {
                SpeechRecognizer.shared.startTranscribing()
                speechSynthesizer.delegate = speechDelegate
                setupKeyboardObservers()
                loadData()
                // Automatically focus the text editor when app loads
                isInputActive = true
                
                // Initialize VoiceManager with saved language
                if !targetLanguageIdentifier.isEmpty {
                    VoiceManager.shared.updateTargetLanguage(to: targetLanguageIdentifier)
                }
            }
            .onChange(of: targetLanguageIdentifier) { _, newValue in
                 if !newValue.isEmpty {
                     VoiceManager.shared.updateTargetLanguage(to: newValue)
                 }
            }
            .onDisappear {
                removeKeyboardObservers()
            }
            // Alerts and Sheets
            .alert("Create New Group", isPresented: $showCreateGroupAlert) {
                TextField("Group Name", text: $newGroupName)
                Button("Cancel", role: .cancel) { }
                Button("Create") {
                    if !newGroupName.isEmpty {
                        groups.append(newGroupName)
                        assignGroupToNote(group: newGroupName)
                        newGroupName = ""
                        saveData()
                    }
                }
            } message: {
                Text("Enter a name for the new group.")
            }
            .alert("Rename Note", isPresented: $showRenameAlert) {
                TextField("Display Name", text: $newDisplayName)
                Button("Cancel", role: .cancel) { }
                Button("Save") {
                    renameNote()
                }
            } message: {
                Text("Enter a new display name for this note in the list.")
            }
            .alert("Rename Group", isPresented: $showRenameGroupAlert) {
                TextField("Group Name", text: $newGroupNameInput)
                Button("Cancel", role: .cancel) { }
                Button("Save") {
                    renameGroup()
                }
            } message: {
                Text("Enter a new name for this group.")
            }

            .sheet(isPresented: $showSettings) {
                SettingsView()
                    .environmentObject(themeManager)
            }
            .confirmationDialog("Select Faves Group", isPresented: $showGroupSelectionSheet, titleVisibility: .visible) {
                ForEach(groups, id: \.self) { group in
                    Button(group) {
                        assignGroupToNote(group: group)
                    }
                }
                Button("New Faves Group") {
                    showCreateGroupAlert = true
                }
            }
            .onShake {
                saveCurrentNote()
                currentNote = ""
                isShowingTranslation = false
                translatedNote = ""
            }
            #if canImport(Translation)
            .translationTask(mainTranslationConfig) { session in
                do {
                    let response = try await session.translate(currentNote)
                    translatedNote = response.targetText
                } catch {
                     print("Translation failed: \(error)")
                     translatedNote = "Translation failed"
                }
            }
            // Split screen: Transcript (foreign → English)
            .translationTask(splitTranscriptConfig) { session in
                do {
                    let response = try await session.translate(speechRecognizer.transcript)
                    translatedTranscript = response.targetText
                } catch {
                    print("Transcript translation failed: \(error)")
                    translatedTranscript = "Translation failed"
                }
            }
            // Split screen: User text (English → foreign)
            .translationTask(splitUserTextConfig) { session in
                do {
                    let response = try await session.translate(currentNote)
                    translatedUserText = response.targetText
                } catch {
                    print("User text translation failed: \(error)")
                    translatedUserText = "Translation failed"
                }
            }
            #endif
            .id(translationTaskID)
        }
    }
    
    private func triggerTranslation() {
        if isShowingTranslation {
            // Revert to original
            isShowingTranslation = false
            translatedTranscript = ""
            translatedUserText = ""
        } else {
            // Trigger Translation
            if isSplitScreenMode {
                // Split screen mode: bidirectional translation
                // Top pane: transcript (foreign language) → English
                // Bottom pane: user text (English) → target language
                guard !speechRecognizer.transcript.isEmpty || !currentNote.isEmpty else { return }
                
                translatedTranscript = "Translating..."
                translatedUserText = "Translating..."
                isShowingTranslation = true
                
                if #available(iOS 18.0, *) {
                    #if canImport(Translation)
                    let langId = targetLanguageIdentifier.isEmpty ? "es-MX" : targetLanguageIdentifier
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        translationTaskID = UUID()
                        
                        // Transcript: foreign → English
                        splitTranscriptConfig = TranslationSession.Configuration(
                            source: Locale.Language(identifier: langId),
                            target: Locale.Language(identifier: "en-US")
                        )
                        
                        // User text: English → foreign
                        splitUserTextConfig = TranslationSession.Configuration(
                            source: Locale.Language(identifier: "en-US"),
                            target: Locale.Language(identifier: langId)
                        )
                    }
                    #else
                    translatedTranscript = "Translation unavailable"
                    translatedUserText = "Translation unavailable"
                    #endif
                } else {
                    translatedTranscript = "Translation requires iOS 18"
                    translatedUserText = "Translation requires iOS 18"
                }
            } else {
                // Normal mode: translate user text to target language
                guard !currentNote.isEmpty else { return }
                translatedNote = "Translating..."
                isShowingTranslation = true
                
                if #available(iOS 18.0, *) {
                    #if canImport(Translation)
                    let langId = targetLanguageIdentifier.isEmpty ? "es-MX" : targetLanguageIdentifier
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        translationTaskID = UUID()
                        mainTranslationConfig = TranslationSession.Configuration(
                            source: Locale.Language(identifier: "en-US"),
                            target: Locale.Language(identifier: langId)
                        )
                    }
                    #else
                    translatedNote = "Translation unavailable"
                    #endif
                } else {
                    translatedNote = "Translation requires iOS 18"
                }
            }
        }
    }
    
    private func assignGroupToNote(group: String) {
        guard let note = noteForGroupSelection else { return }
        if let index = savedNotes.firstIndex(where: { $0.id == note.id }) {
            savedNotes[index].group = group
            saveData()
        }
        noteForGroupSelection = nil
    }
    
    private func renameNote() {
        guard let note = noteForRenaming else { return }
        if let index = savedNotes.firstIndex(where: { $0.id == note.id }) {
            savedNotes[index].displayName = newDisplayName
            saveData()
        }
        noteForRenaming = nil
    }
    
    private func renameGroup() {
        guard let oldName = groupToRename, !newGroupNameInput.isEmpty else { return }
        guard !groups.contains(newGroupNameInput) else { return } // Prevent duplicate names
        
        // Update groups list
        if let index = groups.firstIndex(of: oldName) {
            groups[index] = newGroupNameInput
        }
        
        // Update group colors
        if let color = groupColors[oldName] {
            groupColors[newGroupNameInput] = color
            groupColors.removeValue(forKey: oldName)
        }
        
        // Update notes
        for index in savedNotes.indices {
            if savedNotes[index].group == oldName {
                savedNotes[index].group = newGroupNameInput
            }
        }
        
        saveData()
        groupToRename = nil
        newGroupNameInput = ""
    }
    
    private func setGroupColor(group: String, color: String) {
        groupColors[group] = color
        saveData()
    }
    
    private func getGroupColor(group: String) -> (text: Color, bg: Color) {
        guard let colorName = groupColors[group] else {
            return (.primary, Color(UIColor.systemGray6)) // Default
        }
        
        switch colorName {
        case "Red":
            return (Color(red: 0.5, green: 0, blue: 0), Color(red: 1.0, green: 0.9, blue: 0.9))
        case "Green":
            return (Color(red: 0, green: 0.4, blue: 0), Color(red: 0.9, green: 1.0, blue: 0.9))
        case "Blue":
            return (Color(red: 0, green: 0, blue: 0.5), Color(red: 0.9, green: 0.9, blue: 1.0))
        case "Grey":
            return (Color.black, Color(UIColor.systemGray4))
        default:
            return (.primary, Color(UIColor.systemGray6))
        }
    }
    
    private func deleteNote(_ note: NoteItem) {
        savedNotes.removeAll { $0.id == note.id }
        saveData()
    }
    
    private func deleteNoteFromHistory(_ note: NoteItem) {
        if let index = savedNotes.firstIndex(where: { $0.id == note.id }) {
            if savedNotes[index].group != nil {
                savedNotes[index].isHiddenFromHistory = true
            } else {
                savedNotes.remove(at: index)
            }
            saveData()
        }
    }
    
    private func deleteGroup(_ group: String) {
        // Remove all notes in this group
        savedNotes.removeAll { $0.group == group }
        
        // Remove the group from the groups list
        groups.removeAll { $0 == group }
        
        // Remove the group's color
        groupColors.removeValue(forKey: group)
        
        saveData()
    }
    
    private func adjustFontSize(containerSize: CGSize) {
        let maxFontSize: CGFloat = 40
        let minFontSize: CGFloat = 12
        
        // Determine which text to measure
        let text: String
        if isShowingTranslation && !translatedNote.isEmpty {
            text = translatedNote
        } else {
            text = currentNote.isEmpty ? " " : currentNote
        }
        
        // We need to account for padding in TextEditor (approx 10-20pts usually)
        let horizontalPadding: CGFloat = 16 // Standard padding
        let width = containerSize.width - (horizontalPadding * 2)
        let height = containerSize.height - 90 // Account for top padding (40) + visibility buffer (50)
        
        // Simple binary search or iterative approach to find best fit
        var bestSize = minFontSize
        
        // Check from max down to min
        for size in stride(from: maxFontSize, through: minFontSize, by: -1) {
            let font = UIFont.systemFont(ofSize: size, weight: .bold)
            let attributes = [NSAttributedString.Key.font: font]
            let boundingRect = NSString(string: text).boundingRect(
                with: CGSize(width: width, height: .greatestFiniteMagnitude),
                options: [.usesLineFragmentOrigin, .usesFontLeading],
                attributes: attributes,
                context: nil
            )
            
            if boundingRect.height <= height {
                bestSize = size
                break
            }
        }
        
        if fontSize != bestSize {
            fontSize = bestSize
        }
    }
    
    private func saveCurrentNote() {
        guard !currentNote.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
        
        // Check if the note already exists (even if hidden)
        if savedNotes.contains(where: { $0.content == currentNote }) {
            // If it exists but is hidden, should we unhide it? 
            // The user didn't specify, but typically "saving" again might imply bringing it back.
            // For now, let's just prevent duplicates as requested before.
            return
        }
        
        let newNote = NoteItem(content: currentNote, timestamp: Date())
        savedNotes.insert(newNote, at: 0)
        saveData()
    }
    
    private func clearAllHistory() {
        // Remove notes that don't have a group
        savedNotes.removeAll { $0.group == nil }
        
        // Mark remaining notes (which must have a group) as hidden from history
        for index in savedNotes.indices {
            savedNotes[index].isHiddenFromHistory = true
        }
        saveData()
    }
    

    private func speakText() {
        // Configure Audio Session
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(.playback, mode: .default)
            try audioSession.setActive(true)
        } catch {
            print("Failed to set up audio session: \(error)")
        }

        // Stop any ongoing speech
        if speechSynthesizer.isSpeaking {
            speechSynthesizer.stopSpeaking(at: .immediate)
        }
        
        // Determine text source
        let fullText = (isShowingTranslation && !translatedNote.isEmpty) ? translatedNote : currentNote
        
        // Resume Logic
        var textToSpeak = fullText
        
        // Only attempt resume if NOT in translation mode (simpler to ensure correctness)
        // and if currently showing source text.
        if !isShowingTranslation {
            if !lastSpokenText.isEmpty && fullText.hasPrefix(lastSpokenText) {
                // Resume from where we left off
                let remainingText = String(fullText.dropFirst(lastSpokenText.count))
                
                // If there's nothing new to speak, maybe speak all? Or just nothing?
                // User said "resume from that position to speak the remaining text".
                // If remaining is empty, maybe they just clicked it again. Let's speak all in that case.
                if remainingText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                    textToSpeak = fullText
                    currentUtteranceOffset = 0
                } else {
                    textToSpeak = remainingText
                    currentUtteranceOffset = lastSpokenText.utf16.count
                }
            } else {
                // Text changed significantly, or new start
                currentUtteranceOffset = 0
            }
        } else {
            // Translation mode: always speak full for now to ensure correctness
            currentUtteranceOffset = 0
        }
        
        let utterance = AVSpeechUtterance(string: textToSpeak)
        
        if isShowingTranslation && !translatedNote.isEmpty {
            utterance.voice = VoiceManager.shared.getSelectedTargetVoice()
        } else {
            utterance.voice = VoiceManager.shared.getSelectedEnglishVoice()
        }
        
        utterance.rate = 0.5
        
        // Manual speak still uses currentUtteranceOffset, which we set earlier.
        // We must register it now.
        speechDelegate.register(utterance: utterance, offset: currentUtteranceOffset)
        
        speechSynthesizer.speak(utterance)
        
        // Update state assuming success
        // Careful: if we are speaking a partial chunk, the "lastSpokenText" essentially becomes the full text
        // because next time we want to skip EVERYTHING we just spoke + what we skipped.
        if !isShowingTranslation {
            lastSpokenText = fullText
        } else {
            // If in translation mode, we don't track resume state for now
            lastSpokenText = "" 
        }
    }
    
    private func checkAndSpeakSentences() {
        // Only valid if continuous mode is on, not showing translation, and we have text
        guard isContinuousMode, !isShowingTranslation, !currentNote.isEmpty else { return }
        
        let fullText = currentNote
        
        // Calculate the "new" text that hasn't been spoken yet
        // If lastSpokenText is a prefix, we only look at what's after it.
        // If currentNote changed completely, lastSpokenText might be invalid prefix -> treat as new start?
        // But for "resume", we usually reset lastSpokenText if it doesn't match prefix.
        
        let textToCheck: Substring
        let baseOffset: Int
        
        if fullText.hasPrefix(lastSpokenText) {
            textToCheck = fullText.dropFirst(lastSpokenText.count)
            baseOffset = lastSpokenText.count
        } else {
            // Text changed in a way that invalidated history (e.g. deletion at start)
            // Should we respeak everything? Or just start monitoring from now?
            // "Pick up from last position" implies we monitor new additions.
            // Let's assume we start from scratch.
            textToCheck = Substring(fullText)
            baseOffset = 0
            lastSpokenText = "" // Reset history
        }
        
        let stringToCheck = String(textToCheck)
        
        // Find sentences ending with '.'
        // logic: split by '.', but keep the delimiter? 
        // Or simpler: find ranges of "."
        
        // Robust way: scan for "."
        // We want to speak chunk by chunk.
        // "Hello world. This is test."
        // 1. "Hello world." -> Speak. Update lastSpokenText.
        // 2. " This is test." -> Wait until next period.
        
        var currentIndex = stringToCheck.startIndex
        
        while let range = stringToCheck[currentIndex...].rangeOfCharacter(from: CharacterSet(charactersIn: ".?!")) {
            // Found a period, question mark, or exclamation mark
            let endOfSentence = range.upperBound
            let sentenceSub = stringToCheck[currentIndex..<endOfSentence]
            let sentence = String(sentenceSub)
            
            // Offset for this sentence relative to fullText
            let sentenceStartInSubstring = stringToCheck.distance(from: stringToCheck.startIndex, to: currentIndex)
            let globalOffset = baseOffset + sentenceStartInSubstring
            
            // Create utterance
            let utterance = AVSpeechUtterance(string: sentence)
            utterance.voice = VoiceManager.shared.getSelectedEnglishVoice()
            utterance.rate = 0.5
            
            // Register offset for highlighting
            // We need to use utf16 count for the offset if we want NSRange compatibility
            // But wait, the previous fix used utf16 count of lastSpokenText.
            // Here `globalOffset` is Character based distance.
            
            // Recalculate globalOffset in UTF16
            let preceedingText = fullText.prefix(globalOffset)
            let utf16Offset = preceedingText.utf16.count
            
            speechDelegate.register(utterance: utterance, offset: utf16Offset)
            speechSynthesizer.speak(utterance)
            
            // Advance
            currentIndex = endOfSentence
            
            // Update lastSpokenText immediately so we don't re-queue this sentence
            // Be careful: if we queue multiple, lastSpokenText needs to advance sequentially.
            // Actually, we can just update lastSpokenText to include this sentence.
            // Since we are iterating strictly in order.
            
            // We need to append THIS sentence to lastSpokenText.
            // If lastSpokenText was "A", and we found "B.", new is "AB."
            // But we need to handle the case where we process multiple sentences in one pass.
            // It's safer to reconstruct lastSpokenText from the main string up to current index.
        }
        
        // Update lastSpokenText to point to where we stopped (after the last spoken period)
        let distanceProcessed = stringToCheck.distance(from: stringToCheck.startIndex, to: currentIndex)
        let totalLength = baseOffset + distanceProcessed
        let newLastSpoken = String(fullText.prefix(totalLength))
        
        if newLastSpoken.count > lastSpokenText.count {
            lastSpokenText = newLastSpoken
        }
    }
    
    // Persistence
    private func saveData() {
        do {
            let notesData = try JSONEncoder().encode(savedNotes)
            let groupsData = try JSONEncoder().encode(groups)
            let colorsData = try JSONEncoder().encode(groupColors)
            UserDefaults.standard.set(notesData, forKey: "savedNotes")
            UserDefaults.standard.set(groupsData, forKey: "savedGroups")
            UserDefaults.standard.set(colorsData, forKey: "savedGroupColors")
        } catch {
            print("Error saving data: \(error)")
        }
    }
    
    private func loadData() {
        if let notesData = UserDefaults.standard.data(forKey: "savedNotes") {
            do {
                savedNotes = try JSONDecoder().decode([NoteItem].self, from: notesData)
            } catch {
                print("Error loading notes: \(error)")
            }
        }
        
        if let groupsData = UserDefaults.standard.data(forKey: "savedGroups") {
            do {
                groups = try JSONDecoder().decode([String].self, from: groupsData)
            } catch {
                print("Error loading groups: \(error)")
            }
        }
        
        if let colorsData = UserDefaults.standard.data(forKey: "savedGroupColors") {
            do {
                groupColors = try JSONDecoder().decode([String: String].self, from: colorsData)
            } catch {
                print("Error loading group colors: \(error)")
            }
        }
    }
    
    // Keyboard handling
    private func setupKeyboardObservers() {
        let updateKeyboardHeight: (Notification) -> Void = { notification in
            if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
                self.keyboardHeight = keyboardFrame.height
            }
        }
        
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main, using: updateKeyboardHeight)
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: .main, using: updateKeyboardHeight)
        
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in
            self.keyboardHeight = 0
        }
    }
    
    private func removeKeyboardObservers() {
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    private func mainContentView(geometry: GeometryProxy, keyboardOverlap: CGFloat) -> some View {
        let menuWidth = geometry.size.width * 0.75
        
        return ZStack {
            VStack(spacing: 0) {
                // 1. Top Content (Text Editor + Overlays)
                // Takes all remaining space above buttons
                GeometryReader { textGeo in
                    ZStack(alignment: .topLeading) {
                        if isSplitScreenMode {
                            // SPLIT SCREEN MODE
                            VStack(spacing: 0) {
                                // Top Half: Live Transcript from SpeechRecognizer
                                VStack(alignment: .leading, spacing: 0) {
                                    HStack {
                                        Image(systemName: "person.wave.2")
                                            .font(.system(size: 14))
                                            .foregroundColor(themeManager.secondaryTextColor)
                                        Text("Hearing Person Speaking")
                                            .font(.caption)
                                            .foregroundColor(themeManager.secondaryTextColor)
                                        Spacer()
                                        if speechRecognizer.isRecording {
                                            Circle()
                                                .fill(Color.red)
                                                .frame(width: 8, height: 8)
                                        }
                                    }
                                    .padding(.horizontal)
                                    .padding(.top, 45)
                                    .padding(.bottom, 8)
                                    
                                    ScrollViewReader { proxy in
                                        ScrollView {
                                            // When translating: show transcript translated to English
                                            // When not translating: show original transcript
                                            let displayText: String = {
                                                if isShowingTranslation {
                                                    return translatedTranscript.isEmpty ? "Translating..." : translatedTranscript
                                                } else {
                                                    return speechRecognizer.transcript.isEmpty ? "Listening for speech..." : speechRecognizer.transcript
                                                }
                                            }()
                                            let isPlaceholder = (!isShowingTranslation && speechRecognizer.transcript.isEmpty) || (isShowingTranslation && translatedTranscript.isEmpty)
                                            
                                            Text(displayText)
                                                .font(.system(size: UIDevice.current.userInterfaceIdiom == .pad ? 28 : 18, weight: .medium))
                                                .foregroundColor(isPlaceholder ? themeManager.secondaryTextColor : themeManager.textColor)
                                                .frame(maxWidth: .infinity, alignment: .leading)
                                                .padding(.horizontal)
                                                .padding(.bottom, 10)
                                                .id("transcriptBottom")
                                        }
                                        .onAppear {
                                            // Scroll to bottom when entering split screen
                                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                                                proxy.scrollTo("transcriptBottom", anchor: .bottom)
                                            }
                                        }
                                        .onChange(of: speechRecognizer.transcript) {
                                            withAnimation {
                                                proxy.scrollTo("transcriptBottom", anchor: .bottom)
                                            }
                                        }
                                        .onChange(of: translatedTranscript) {
                                            withAnimation {
                                                proxy.scrollTo("transcriptBottom", anchor: .bottom)
                                            }
                                        }
                                    }
                                }
                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                .background(themeManager.backgroundColor.opacity(0.95))
                                
                                // Divider
                                Rectangle()
                                    .fill(themeManager.secondaryTextColor.opacity(0.3))
                                    .frame(height: 2)
                                
                                // Bottom Half: User Typed Text
                                VStack(alignment: .leading, spacing: 0) {
                                    HStack {
                                        Image(systemName: "keyboard")
                                            .font(.system(size: 14))
                                            .foregroundColor(themeManager.secondaryTextColor)
                                        Text("You Are Typing")
                                            .font(.caption)
                                            .foregroundColor(themeManager.secondaryTextColor)
                                        Spacer()
                                    }
                                    .padding(.horizontal)
                                    .padding(.top, 8)
                                    .padding(.bottom, 8)
                                    
                                    ZStack(alignment: .topLeading) {
                                        if isShowingTranslation {
                                            // Show user text translated to target language (read-only)
                                            ScrollViewReader { proxy in
                                                ScrollView {
                                                    let displayText = translatedUserText.isEmpty ? "Translating..." : translatedUserText
                                                    Text(displayText)
                                                        .font(.system(size: UIDevice.current.userInterfaceIdiom == .pad ? 28 : 18, weight: .medium))
                                                        .foregroundColor(translatedUserText.isEmpty ? themeManager.secondaryTextColor : themeManager.textColor)
                                                        .frame(maxWidth: .infinity, alignment: .leading)
                                                        .padding(.horizontal)
                                                        .padding(.bottom, 10)
                                                        .id("typedTextBottom")
                                                }
                                                .onAppear {
                                                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                                                        withAnimation {
                                                            proxy.scrollTo("typedTextBottom", anchor: .bottom)
                                                        }
                                                    }
                                                }
                                                .onChange(of: translatedUserText) {
                                                    withAnimation {
                                                        proxy.scrollTo("typedTextBottom", anchor: .bottom)
                                                    }
                                                }
                                            }
                                        } else {
                                            // Show TextEditor for user input - Native TextEditor handles newlines
                                            TextEditor(text: $currentNote)
                                                .font(.system(size: UIDevice.current.userInterfaceIdiom == .pad ? 28 : 18, weight: .medium))
                                                .foregroundColor(themeManager.textColor)
                                                .focused($isInputActive)
                                                .scrollContentBackground(.hidden)
                                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                                .padding(.horizontal, 12)
                                                .onChange(of: currentNote) {
                                                    // Auto-speak in continuous mode (same as main screen)
                                                    if isContinuousMode {
                                                        checkAndSpeakSentences()
                                                    }
                                                }
                                        }
                                        
                                        // Speech Highlight Overlay for split screen (covers TextEditor)
                                        if speechDelegate.isSpeaking && !isShowingTranslation {
                                            if let range = speechDelegate.characterRange {
                                                let adjustedLocation = range.location + currentUtteranceOffset
                                                let adjustedRange = NSRange(location: adjustedLocation, length: range.length)
                                                
                                                ScrollView {
                                                    Text(attributedText(for: currentNote, highlighting: adjustedRange))
                                                        .font(.system(size: UIDevice.current.userInterfaceIdiom == .pad ? 28 : 18, weight: .medium))
                                                        .foregroundColor(themeManager.textColor)
                                                        .frame(maxWidth: .infinity, alignment: .leading)
                                                        .padding(.horizontal)
                                                        .padding(.bottom, 10)
                                                }
                                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                                .background(themeManager.backgroundColor)
                                                .allowsHitTesting(false)
                                            }
                                        }
                                        
                                        // Placeholder when empty (only show when not translating and note is empty)
                                        if currentNote.isEmpty && !isShowingTranslation && !speechDelegate.isSpeaking {
                                            Text("Start typing here...")
                                                .font(.system(size: UIDevice.current.userInterfaceIdiom == .pad ? 28 : 18, weight: .medium))
                                                .foregroundColor(themeManager.secondaryTextColor)
                                                .padding(.horizontal, 16) // Match TextEditor padding
                                                .padding(.top, 8)
                                                .allowsHitTesting(false)
                                        }
                                    }
                                }
                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                .clipped()  // Prevent content from extending beyond bounds
                                .background(themeManager.backgroundColor)
                            }
                            .layoutPriority(1)  // Ensure split screen takes priority in layout
                            .background(themeManager.backgroundColor)
                            .colorScheme(themeManager.currentTheme == .light ? .light : .dark)
                            // Swipe gestures for split screen
                            .gesture(
                                DragGesture()
                                    .onEnded { value in
                                        if value.translation.height > 50 {
                                            // Swipe Down: Dismiss Keyboard
                                            isInputActive = false
                                        } else if value.translation.width < -50 {
                                            // Swipe Left: Clear both user text and transcript
                                            saveCurrentNote()
                                            currentNote = ""
                                            speechRecognizer.clear()
                                            translatedNote = ""
                                            translatedTranscript = ""
                                            translatedUserText = ""
                                            isShowingTranslation = false
                                            lastSpokenText = ""
                                            currentUtteranceOffset = 0
                                        } else if value.translation.width > 50 {
                                            // Swipe Right: Go to full screen VoiceNoteView
                                            withAnimation(.easeInOut(duration: 0.6)) {
                                                isVoiceScreenActive = true
                                            }
                                        }
                                    }
                            )
                        } else {
                            // NORMAL FULL SCREEN MODE
                            TextEditor(text: $currentNote)
                                .scrollDisabled(true)
                                .focused($isInputActive)
                                .font(.system(size: fontSize, weight: .bold))
                                .foregroundColor(themeManager.textColor)
                                .padding(.horizontal)
                                .padding(.top, 40) // Avoid overlap with Clear All button
                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                .scrollContentBackground(.hidden) // Allow custom background
                                .background(themeManager.backgroundColor)
                                .colorScheme(themeManager.currentTheme == .light ? .light : .dark)
                                .onChange(of: currentNote) {
                                    adjustFontSize(containerSize: textGeo.size)
                                }
                                .onChange(of: translatedNote) {
                                    adjustFontSize(containerSize: textGeo.size)
                                }
                                .onChange(of: isShowingTranslation) {
                                    adjustFontSize(containerSize: textGeo.size)
                                }
                                // We trigger font adjustment when the frame size changes (e.g. keyboard appears/disappears)
                                .onChange(of: textGeo.size) {
                                    adjustFontSize(containerSize: textGeo.size)
                                }
                                // Center Swipe Gestures
                                .gesture(
                                    DragGesture()
                                        .onEnded { value in
                                            // Check if it's a center swipe (not edge)
                                            let startX = value.startLocation.x
                                            let isEdgeSwipe = startX < 50 || startX > geometry.size.width - 50
                                            
                                            if !isEdgeSwipe {
                                                if value.translation.height > 50 {
                                                    // Swipe Down: Dismiss Keyboard
                                                    isInputActive = false
                                                } else if value.translation.width < -50 {
                                                    // Swipe Left: Clear Text
                                                    saveCurrentNote()
                                                    currentNote = ""
                                                    // Also clear translation state
                                                    isShowingTranslation = false
                                                    translatedNote = ""
                                                    lastSpokenText = ""
                                                    currentUtteranceOffset = 0
                                                } else if value.translation.width > 50 {
                                                    // Swipe Right: Flip to Voice Screen
                                                    withAnimation(.easeInOut(duration: 0.6)) {
                                                        isVoiceScreenActive = true
                                                    }
                                                }
                                            }
                                        }
                                )
                            
                                // Auto-speak monitor
                                .onChange(of: currentNote) {
                                    if isContinuousMode {
                                        checkAndSpeakSentences()
                                    }
                                }
                            
                            // Speech Highlight Overlay
                            
                            // Speech Highlight Overlay
                            if speechDelegate.isSpeaking {
                                let textToHighlight = (isShowingTranslation && !translatedNote.isEmpty) ? translatedNote : currentNote
                                
                                // Only overlay if we have a range to highlight
                                if let range = speechDelegate.characterRange {
                                    // Adjust range by offset
                                    let adjustedLocation = range.location + currentUtteranceOffset
                                    let adjustedRange = NSRange(location: adjustedLocation, length: range.length)
                                    
                                    ScrollView {
                                        Text(attributedText(for: textToHighlight, highlighting: adjustedRange))
                                            .font(.system(size: fontSize, weight: .bold))
                                            .foregroundColor(themeManager.textColor)
                                            .padding(.horizontal) // Match TextEditor's 16pt default approximately
                                            .padding(.top, 40) // Match padding
                                            .frame(maxWidth: .infinity, alignment: .leading)
                                    }
                                    .padding(.horizontal, 5) // TextEditor has distinct internal padding, adjusting slightly
                                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                                    .background(themeManager.backgroundColor) // Cover the editor
                                    .allowsHitTesting(false) // Pass touches through
                                }
                            }
                            
                            // 2. Translation View (Overlay)
                            if isShowingTranslation {
                                ScrollView {
                                    Text(translatedNote.isEmpty ? "Translating..." : translatedNote)
                                        .font(.system(size: fontSize, weight: .bold))
                                        .foregroundColor(themeManager.textColor)
                                        .padding(.horizontal)
                                        .padding(.top, 40)
                                        .frame(maxWidth: .infinity, alignment: .leading)
                                }
                                .scrollDisabled(true)
                                .background(themeManager.backgroundColor)
                                .gesture(
                                    DragGesture()
                                        .onEnded { value in
                                            if value.translation.width < -50 {
                                                // Swipe Left: Clear Text
                                                saveCurrentNote()
                                                currentNote = ""
                                                translatedNote = ""
                                                isShowingTranslation = false
                                                lastSpokenText = ""
                                                currentUtteranceOffset = 0
                                                
                                                // Focus input
                                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                                                    isInputActive = true
                                                }
                                            } else if value.translation.width > 50 {
                                                // Swipe Right: Flip to Voice Screen
                                                withAnimation(.easeInOut(duration: 0.6)) {
                                                    isVoiceScreenActive = true
                                                }
                                            }
                                        }
                                )
                            }
                        }
                        
                        // Placeholder text (hidden in split screen mode)
                        if currentNote.isEmpty && !isSplitScreenMode {
                            VStack(spacing: 10) {
                                VStack(spacing: 8) {
                                    Text("Start Typing")
                                        .font(.system(size: 30, weight: .bold))
                                        .foregroundColor(themeManager.secondaryTextColor.opacity(0.8))
                                    
                                    Text("(shake to start over)")
                                        .font(.system(size: 16))
                                        .foregroundColor(themeManager.secondaryTextColor.opacity(0.8))
                                }
                                .padding(.bottom, 20)

                                Image(systemName: "arrow.right")
                                    .font(.system(size: fontSize * 0.8, weight: .bold))
                                    .foregroundColor(themeManager.secondaryTextColor)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .padding(.leading, 20)

                                Text("swipe from left edge for History")
                                    .font(.system(size: fontSize * 0.5, weight: .medium))
                                    .foregroundColor(themeManager.secondaryTextColor)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .padding(.leading, 20)
                                
                                Text("swipe from right edge for Favorites")
                                    .font(.system(size: fontSize * 0.5, weight: .medium))
                                    .foregroundColor(themeManager.secondaryTextColor)
                                    .frame(maxWidth: .infinity, alignment: .trailing)
                                    .padding(.trailing, 20)

                                Image(systemName: "arrow.left")
                                    .font(.system(size: fontSize * 0.8, weight: .bold))
                                    .foregroundColor(themeManager.secondaryTextColor)
                                    .frame(maxWidth: .infinity, alignment: .trailing)
                                    .padding(.trailing, 20)
                            }
                            .padding()
                            .padding(.top, 100) // Add some top padding to position it "upper middle"
                            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
                            .allowsHitTesting(false)
                        }
                        
                        // Top bar with Split Screen toggle and Clear all button
                        HStack {
                            Spacer()
                            
                            // Split Screen Toggle (Center)
                            Button(action: {
                                withAnimation(.easeInOut(duration: 0.3)) {
                                    isSplitScreenMode.toggle()
                                }
                                // Show keyboard when entering split screen mode (triggers layout fix)
                                if isSplitScreenMode {
                                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                                        isInputActive = true
                                    }
                                }
                            }) {
                                Image(systemName: isSplitScreenMode ? "rectangle" : "rectangle.split.1x2")
                                    .font(.system(size: 22))
                                    .foregroundColor(themeManager.textColor.opacity(0.7))
                                    .padding(10)
                            }
                            
                            Spacer()
                            
                            // Clear all button (Right) - also show in split mode if there's transcript
                            if !currentNote.isEmpty || (isSplitScreenMode && !speechRecognizer.transcript.isEmpty) {
                                Button(action: {
                                    saveCurrentNote()
                                    currentNote = ""
                                    translatedNote = ""
                                    isShowingTranslation = false
                                    lastSpokenText = ""
                                    currentUtteranceOffset = 0
                                    
                                    // In split screen mode, also clear transcript
                                    if isSplitScreenMode {
                                        speechRecognizer.clear()
                                        translatedTranscript = ""
                                        translatedUserText = ""
                                    }
                                    
                                    // Dismiss keyboard to reset layout state
                                    isInputActive = false
                                }) {
                                    Text("Clear all")
                                        .foregroundColor(.red)
                                        .padding()
                                }
                            }
                        }
                        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
                        .padding(.horizontal, 10)
                        .padding(.top, 5)
                    }
                } // End GeometryReader (Text Area)
                
                // 3. Bottom Buttons
                // Stacked below text editor
                HStack(spacing: 10) {
                        Spacer(minLength: 0)
                        
                        // Translate Button (show in split mode or when there's text)
                        if !currentNote.isEmpty || (isSplitScreenMode && !speechRecognizer.transcript.isEmpty) {
                            Button(action: {
                                if targetLanguageIdentifier.isEmpty {
                                    showSettings = true 
                                } else {
                                    triggerTranslation()
                                }
                            }) {
                                Group {
                                    if isShowingTranslation {
                                        Text("Original 🇺🇸")
                                    } else {
                                        if let lang = languageManager.supportedLanguages.first(where: { $0.id == targetLanguageIdentifier }) {
                                            Text("Translate to \(lang.flag)")
                                        } else {
                                            if targetLanguageIdentifier.isEmpty {
                                                 Text("Translate")
                                            } else {
                                                 Text("Translate to 🇲🇽") 
                                            }
                                        }
                                    }
                                }
                                    .font(.system(size: 16, weight: .medium))
                                    .fixedSize(horizontal: true, vertical: false)
                                    .foregroundColor(.white)
                                    .padding(.horizontal, 16)
                                    .padding(.vertical, 12)
                                    .background(Color.blue.opacity(0.6))
                                    .cornerRadius(25)
                            }
                        }
                        
                        // "Speak for me" Button
                        if !currentNote.isEmpty {
                            Button(action: {
                                if isShowingTranslation {
                                    // Translation Mode: Simple Speak Toggle
                                    if speechDelegate.isSpeaking {
                                        speechSynthesizer.stopSpeaking(at: .immediate)
                                    } else {
                                        speakText()
                                    }
                                } else {
                                    // Original Continuous Monitor Logic
                                    if isContinuousMode {
                                        // Turn off continuous mode
                                        isContinuousMode = false
                                        speechSynthesizer.stopSpeaking(at: .immediate)
                                    } else {
                                        if !speechSynthesizer.isSpeaking {
                                            // "Start monitoring" -> Enable continuous mode
                                            isContinuousMode = true
                                            // Speak current content immediately (manual resume style) AND start monitoring
                                            speakText()
                                        } else {
                                            // If already speaking manually, stop
                                            speechSynthesizer.stopSpeaking(at: .immediate)
                                        }
                                    }
                                }
                            }) {
                                Text(isShowingTranslation ? (speechDelegate.isSpeaking ? "Speaking..." : "Speak") : (isContinuousMode ? "Speaking..." : (speechDelegate.isSpeaking ? "Speaking..." : "Speak for me")))
                                    .font(.system(size: 16, weight: .medium))
                                    .fixedSize(horizontal: true, vertical: false)
                                    .foregroundColor(.white)
                                    .padding(.horizontal, 12)
                                    .padding(.vertical, 12)
                                    .background(
                                        ZStack {
                                            if isShowingTranslation {
                                                // Simple Blue/Cyan for Translation
                                                if speechDelegate.isSpeaking {
                                                    Color.cyan
                                                        .shadow(color: .cyan, radius: isSpeakingAnimation ? 30 : 2)
                                                } else {
                                                    Color.blue.opacity(0.6)
                                                }
                                            } else {
                                                // Continuous Mode Colors
                                                if isContinuousMode {
                                                    Color.green
                                                } else if speechDelegate.isSpeaking {
                                                    Color.cyan
                                                        .shadow(color: .cyan, radius: isSpeakingAnimation ? 30 : 2)
                                                } else {
                                                    Color.blue.opacity(0.6)
                                                }
                                            }
                                        }
                                    )
                                    .cornerRadius(25)
                                    .overlay(
                                        RoundedRectangle(cornerRadius: 25)
                                            .stroke(Color.white.opacity(0.5), lineWidth: speechDelegate.isSpeaking ? 1 : 0)
                                    )
                                    .scaleEffect(isSpeakingAnimation ? 1.1 : 1.0)
                            }
                            .onChange(of: speechDelegate.isSpeaking) { oldValue, speaking in
                                if speaking {
                                    withAnimation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true)) {
                                        isSpeakingAnimation = true
                                    }
                                } else {
                                    withAnimation(.default) {
                                        isSpeakingAnimation = false
                                    }
                                }
                            }
                        }

                        // Flip Button
                        Button(action: {
                            withAnimation(.easeInOut(duration: 0.6)) {
                                isVoiceScreenActive = true
                            }
                        }) {
                            Text("Flip")
                                .font(.system(size: 16, weight: .medium))
                                .fixedSize(horizontal: true, vertical: false)
                                .foregroundColor(.white)
                                .padding(.horizontal, 20)
                                .padding(.vertical, 12)
                                .background(Color.blue)
                                .cornerRadius(25)
                        }
                    }
                    .padding(.trailing, 8)
                    .padding(.bottom, keyboardHeight > 0 ? 10 : geometry.safeAreaInsets.bottom + 10)
                    .padding(.top, isSplitScreenMode ? 3 : 10) // Small padding in split screen mode
                    .background(Color.black)
                    .allowsHitTesting(!isMenuOpen && !isGroupsMenuOpen)
            }
            .onChange(of: isSplitScreenMode) { oldValue, newValue in
                // Force layout recalculation when entering split screen
                if newValue {
                    // Simulate typing to trigger layout update, which user confirmed fixes the gap
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        let original = currentNote
                        // Only trigger if text is not empty to avoid placeholder glitches, or handle empty case
                        if !original.isEmpty {
                            currentNote = original + " "
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                                currentNote = original
                            }
                        } else {
                            // If empty, we can toggle a dummy character since placeholder might be showing
                            currentNote = " "
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                                currentNote = ""
                            }
                        }
                    }
                }
            }
            .onChange(of: isVoiceScreenActive) { oldValue, newValue in
                // Force layout recalculation when returning from voice screen to split screen
                if !newValue && isSplitScreenMode {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        let original = currentNote
                        if !original.isEmpty {
                            currentNote = original + " "
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                                currentNote = original
                            }
                        } else {
                            currentNote = " "
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                                currentNote = ""
                            }
                        }
                    }
                }
            }
            // View-Level Enforcement:
            // iPhone: Force 0 padding if in Landscape OR Hardware Keyboard attached (Strict Mode).
            // iPad: Allow padding so the shortcut bar doesn't cover content.
            .padding(.bottom, (UIDevice.current.userInterfaceIdiom != .pad && (geometry.size.width > geometry.size.height || isHardwareKeyboardAttached)) ? 0 : (keyboardHeight > 0 ? keyboardHeight : 0))
            .background(Color.black) // Ensure keyboard area is black
            .offset(x: isMenuOpen ? menuWidth : (isGroupsMenuOpen ? -menuWidth : 0))
            .animation(.easeInOut, value: isMenuOpen)
            .animation(.easeInOut, value: isGroupsMenuOpen)
            .transition(.modifier(
                active: FlipTransition(angle: -90),
                identity: FlipTransition(angle: 0)
            ))
        }
            .ignoresSafeArea(.all, edges: .bottom)
        .overlay {
            // Blocking Overlay for Landscape Mode without Hardware Keyboard
            GeometryReader { proxy in
                if proxy.size.width > proxy.size.height && !isHardwareKeyboardAttached {
                    ZStack {
                        themeManager.backgroundColor.ignoresSafeArea()
                        
                        VStack(spacing: 20) {
                            Image(systemName: "keyboard.badge.exclamationmark")
                                .font(.system(size: 60))
                                .foregroundColor(themeManager.secondaryTextColor)
                            
                            Text("Landscape Not Supported")
                                .font(.title)
                                .bold()
                                .foregroundColor(themeManager.textColor)
                            
                            Text("Please rotate your device to portrait mode\nor connect a hardware keyboard.")
                                .multilineTextAlignment(.center)
                                .font(.body)
                                .foregroundColor(themeManager.secondaryTextColor)
                        }
                        .padding()
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(themeManager.backgroundColor)
                    .task {
                        // Dismiss keyboard immediately when blocking overlay appears
                        isInputActive = false
                        keyboardHeight = 0
                    }
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
            // Ghost Keyboard Filter & Landscape Policy:
            if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
                let isLandscape = UIScreen.main.bounds.width > UIScreen.main.bounds.height
                let isHardware = isHardwareKeyboardAttached || GCKeyboard.coalesced != nil
                let isPad = UIDevice.current.userInterfaceIdiom == .pad
                
                // Policy:
                // iPhone: In landscape or if HW keyboard attached, strict 0 height.
                // iPad: Allow system height (accounts for shortcut bar).
                if !isPad && (isLandscape || isHardware) {
                     self.keyboardHeight = 0
                } else {
                    self.keyboardHeight = keyboardFrame.height
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)) { notification in
             if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
                 let isLandscape = UIScreen.main.bounds.width > UIScreen.main.bounds.height
                 let isHardware = isHardwareKeyboardAttached || GCKeyboard.coalesced != nil
                 let isPad = UIDevice.current.userInterfaceIdiom == .pad
                 
                 if !isPad && (isLandscape || isHardware) {
                       self.keyboardHeight = 0
                 } else {
                     self.keyboardHeight = keyboardFrame.height
                 }
             }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
            self.keyboardHeight = 0
        }
        .onReceive(NotificationCenter.default.publisher(for: .GCKeyboardDidConnect)) { _ in
            isHardwareKeyboardAttached = true
            // Reset layout if "squashed"; assume 0 height for hardware kb initially
            self.keyboardHeight = 0
        }
        .onReceive(NotificationCenter.default.publisher(for: .GCKeyboardDidDisconnect)) { _ in
            isHardwareKeyboardAttached = false
            // If we are in landscape, this will trigger the overlay, which should dismiss KB.
            // But let's let the geometry/state change handler do that.
        }
        .onAppear {
            // Existing onAppear logic
            SpeechRecognizer.shared.startTranscribing()
            speechSynthesizer.delegate = speechDelegate
            // setupKeyboardObservers() -- REMOVED
            loadData()
            isInputActive = true
            
            if !targetLanguageIdentifier.isEmpty {
                VoiceManager.shared.updateTargetLanguage(to: targetLanguageIdentifier)
            }
            
            // Hardware Keyboard Detection
            isHardwareKeyboardAttached = GCKeyboard.coalesced != nil
        }
        .onChange(of: isHardwareKeyboardAttached) { _, attached in
             // If detached while in landscape -> Blocking overlay appears -> Dismiss KB
             // We can check geometry here via GeometryReader but we are outside of it.
             // We rely on the overlay's existence or separate check? 
             // Simplest: The overlay itself can have a .task / .onAppear
        }
    }

    private func attributedText(for text: String, highlighting nsRange: NSRange?) -> AttributedString {
        // Fallback or base style
        let baseColor = themeManager.textColor
        
        guard let nsRange = nsRange,
              let range = Range(nsRange, in: text) else {
            var full = AttributedString(text)
            full.foregroundColor = baseColor
            return full
        }
        
        // Construct by parts to ensure we target the EXACT range
        // rather than searching for the substring content
        let prefix = text[..<range.lowerBound]
        let target = text[range]
        let suffix = text[range.upperBound...]
        
        var result = AttributedString(prefix)
        result.foregroundColor = baseColor
        
        var middle = AttributedString(target)
        middle.foregroundColor = .gray
        
        var end = AttributedString(suffix)
        end.foregroundColor = baseColor
        
        result.append(middle)
        result.append(end)
        
        return result
    }
}

struct SideMenuView: View {
    let notes: [NoteItem]
    let onSelect: (NoteItem) -> Void
    let onStarTap: (NoteItem) -> Void
    let onClearAll: () -> Void
    let onDelete: (NoteItem) -> Void
    let onSettings: () -> Void
    let onClose: () -> Void
    
    var body: some View {
        HStack(spacing: 0) {
            ZStack {
                Color.black
                    .contentShape(Rectangle())
                    .gesture(
                        DragGesture()
                            .onEnded { value in
                                if value.translation.width < -50 {
                                    onClose()
                                }
                            }
                    )
                
                VStack(alignment: .leading) {
                    HStack {
                        Text("History")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                        Spacer()
                        Button("Clear All") {
                            onClearAll()
                        }
                        .foregroundColor(.red)
                    }
                    .padding(.top, 50)
                    .padding(.horizontal)
                    .contentShape(Rectangle())
                    .gesture(
                        DragGesture()
                            .onEnded { value in
                                if value.translation.width < -50 {
                                    onClose()
                                }
                            }
                    )
                    
                    List {
                        ForEach(notes) { note in
                            HStack {
                                Button(action: {
                                    onSelect(note)
                                }) {
                                    VStack(alignment: .leading) {
                                        Text(note.content)
                                            .lineLimit(1)
                                            .font(.headline)
                                            .foregroundColor(.white)
                                        Text(note.timestamp, style: .time)
                                            .font(.caption)
                                            .foregroundColor(.gray)
                                    }
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .contentShape(Rectangle())
                                }
                                .buttonStyle(.plain)
                                
                                Spacer()
                                
                                Button(action: {
                                    onStarTap(note)
                                }) {
                                    Image(systemName: note.group != nil ? "star.fill" : "star")
                                        .foregroundColor(note.group != nil ? .yellow : .gray)
                                }
                                .buttonStyle(BorderlessButtonStyle())
                            }
                            .listRowBackground(Color.black)
                        }
                        .onDelete { indexSet in
                            for index in indexSet {
                                onDelete(notes[index])
                            }
                        }
                        
                        // Footer to catch clicks in empty space
                        Section {
                            Color.clear
                                .contentShape(Rectangle())
                                .frame(height: 1000)
                                .listRowBackground(Color.clear)
                                .listRowSeparator(.hidden)
                                .gesture(
                                    DragGesture()
                                        .onEnded { value in
                                            if value.translation.width < -50 {
                                                onClose()
                                            }
                                        }
                                )
                        }
                    }
                    .listStyle(.plain)
                    .scrollContentBackground(.hidden)
                    
                    Spacer()
                    
                    Button(action: onSettings) {
                        HStack {
                            Image(systemName: "gearshape.fill")
                            Text("Settings")
                                .bold()
                        }
                        .foregroundColor(.white)
                        .padding()
                    }
                }
            }
            
            Rectangle()
                .fill(Color.white)
                .frame(width: 1)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct GroupsMenuView: View {
    let groups: [String]
    let groupColors: [String: String]
    let notes: [NoteItem]
    let getGroupColor: (String) -> (text: Color, bg: Color)
    let onSelect: (NoteItem) -> Void
    let onRenameNote: (NoteItem) -> Void
    let onRenameGroup: (String) -> Void
    let onColorGroup: (String, String) -> Void
    let onDelete: (NoteItem) -> Void
    let onDeleteGroup: (String) -> Void
    let onClose: () -> Void
    
    var body: some View {
        HStack(spacing: 0) {
            Rectangle()
                .fill(Color.white)
                .frame(width: 1)
                
            ZStack {
                Color.black
                    .contentShape(Rectangle())
                    .gesture(
                        DragGesture()
                            .onEnded { value in
                                if value.translation.width > 50 {
                                    onClose()
                                }
                            }
                    )
                
                VStack(alignment: .leading) {
                    Text("Faves")
                        .font(.title)
                        .bold()
                        .foregroundColor(.white)
                        .padding(.top, 50)
                        .padding(.leading)
                        .contentShape(Rectangle())
                        .gesture(
                            DragGesture()
                                .onEnded { value in
                                    if value.translation.width > 50 {
                                        onClose()
                                    }
                                }
                        )
                    
                    List {
                        ForEach(groups, id: \.self) { group in
                            let colors = getGroupColor(group)
                            
                            Section {
                                // Group Header Row (interactive)
                                HStack {
                                    Text(group)
                                        .font(.headline)
                                        .foregroundColor(colors.text)
                                        .frame(maxWidth: .infinity, alignment: .leading)
                                        .contentShape(Rectangle())
                                        .onTapGesture {} // Consume tap to prevent closing
                                }
                                .padding(.vertical, 8)
                                .listRowBackground(colors.bg)
                                .swipeActions(edge: .trailing, allowsFullSwipe: true) {
                                    Button(role: .destructive) {
                                        onDeleteGroup(group)
                                    } label: {
                                        Label("Delete", systemImage: "trash")
                                    }
                                }
                                .contextMenu {
                                    Button {
                                        onRenameGroup(group)
                                    } label: {
                                        Label("Rename", systemImage: "pencil")
                                    }
                                    
                                    Menu("Color") {
                                        Button("Grey") {
                                            onColorGroup(group, "Grey")
                                        }
                                        Button("Green") {
                                            onColorGroup(group, "Green")
                                        }
                                        Button("Red") {
                                            onColorGroup(group, "Red")
                                        }
                                        Button("Blue") {
                                            onColorGroup(group, "Blue")
                                        }
                                    }
                                }
                                
                                // Notes under this group
                                ForEach(notes.filter { $0.group == group }) { note in
                                    Text(note.displayName ?? note.content)
                                        .lineLimit(1)
                                        .foregroundColor(colors.text)
                                        .onTapGesture {
                                            onSelect(note)
                                        }
                                    .listRowBackground(colors.bg)
                                    .contextMenu {
                                        Button {
                                            onRenameNote(note)
                                        } label: {
                                            Label("Rename", systemImage: "pencil")
                                        }
                                    }
                                }
                                .onDelete { indexSet in
                                    let filteredNotes = notes.filter { $0.group == group }
                                    for index in indexSet {
                                        let noteToDelete = filteredNotes[index]
                                        onDelete(noteToDelete)
                                    }
                                }
                            }
                        }
                        
                        // Footer to catch clicks in empty space
                        Section {
                            Color.clear
                                .contentShape(Rectangle())
                                .frame(height: 1000)
                                .listRowBackground(Color.clear)
                                .listRowSeparator(.hidden)
                                .gesture(
                                    DragGesture()
                                        .onEnded { value in
                                            if value.translation.width > 50 {
                                                onClose()
                                            }
                                        }
                                )
                        }
                    }
                    .listStyle(.insetGrouped)
                    .scrollContentBackground(.hidden)
                    
                    Spacer()
                }
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
}

#Preview {
    ContentView()
}

// Extensions for Shake Gesture
extension UIDevice {
    static let deviceDidShakeNotification = Notification.Name(rawValue: "deviceDidShakeNotification")
}

extension UIWindow {
    open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        if motion == .motionShake {
            NotificationCenter.default.post(name: UIDevice.deviceDidShakeNotification, object: nil)
        }
    }
}

struct DeviceShakeViewModifier: ViewModifier {
    let action: () -> Void

    func body(content: Content) -> some View {
        content
            .onAppear()
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.deviceDidShakeNotification)) { _ in
                action()
            }
    }
}

extension View {
    func onShake(perform action: @escaping () -> Void) -> some View {
        self.modifier(DeviceShakeViewModifier(action: action))
    }
}

struct FlipTransition: GeometryEffect {
    var angle: Double
    var axis: (x: CGFloat, y: CGFloat) = (x: 0, y: 1)
    
    var animatableData: Double {
        get { angle }
        set { angle = newValue }
    }
    
    func effectValue(size: CGSize) -> ProjectionTransform {
        let a = CGFloat(Angle(degrees: angle).radians)
        
        var transform3d = CATransform3DIdentity
        transform3d.m34 = -1 / 500 // Perspective
        
        transform3d = CATransform3DRotate(transform3d, a, axis.x, axis.y, 0)
        transform3d = CATransform3DTranslate(transform3d, -size.width/2.0, -size.height/2.0, 0)
        
        let affineTransform = ProjectionTransform(CGAffineTransform(translationX: size.width/2.0, y: size.height/2.0))
        
        return ProjectionTransform(transform3d).concatenating(affineTransform)
    }
}

class SpeechDelegate: NSObject, ObservableObject, AVSpeechSynthesizerDelegate {
    @Published var isSpeaking = false
    @Published var characterRange: NSRange?
    
    // Map to store offsets for each utterance
    private var utteranceOffsets: [AVSpeechUtterance: Int] = [:]
    
    func register(utterance: AVSpeechUtterance, offset: Int) {
        utteranceOffsets[utterance] = offset
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
        let offset = utteranceOffsets[utterance] ?? 0
        DispatchQueue.main.async {
            // Apply offset to create a global range
            self.characterRange = NSRange(location: characterRange.location + offset, length: characterRange.length)
        }
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
        DispatchQueue.main.async {
            self.isSpeaking = true
            // Don't reset range here, wait for willSpeakRange
        }
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        DispatchQueue.main.async {
            // Only stop if this was the last thing (or if queue is empty? AVSpeechSynthesizer manages queue)
            // But we can check if synthesizer.isSpeaking, although the delegate is called per utterance.
            // If we have more in queue, isSpeaking remains true? 
            // Actually, synthesizer.isSpeaking returns true if there are more utterances.
            
            // Clean up offset
            self.utteranceOffsets.removeValue(forKey: utterance)
            
            if !synthesizer.isSpeaking {
                self.isSpeaking = false
                self.characterRange = nil
            }
        }
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
        DispatchQueue.main.async {
            self.utteranceOffsets.removeAll()
            self.isSpeaking = false
            self.characterRange = nil
        }
    }
}
