package com.openclaw.alfred.ui.screens

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.ui.window.DialogProperties
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Send
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material.icons.filled.VolumeOff
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.AttachFile
import androidx.compose.material3.*
import androidx.compose.ui.window.Dialog
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.size
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.Dp
import kotlin.math.abs
import com.openclaw.alfred.gateway.GatewayClient
import com.openclaw.alfred.gateway.GatewayListener
import com.openclaw.alfred.voice.VoiceInputManager
import com.openclaw.alfred.voice.TTSManager
import com.openclaw.alfred.voice.WakeWordDetector
import com.openclaw.alfred.voice.VoskRecognitionManager
import com.openclaw.alfred.permissions.PermissionHelper
import com.openclaw.alfred.notifications.NotificationHelper
import com.openclaw.alfred.storage.ConversationStorage
import com.openclaw.alfred.storage.NotificationStorage
import com.openclaw.alfred.alarm.AlarmManager
import com.openclaw.alfred.alarm.AlarmActivity
import androidx.compose.ui.platform.LocalContext
import android.content.Intent
import android.content.ServiceConnection
import android.content.ComponentName
import android.os.IBinder
import android.app.PendingIntent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import android.Manifest
import android.widget.Toast
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.media.ToneGenerator
import android.media.AudioManager
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import org.json.JSONObject
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.openclaw.alfred.BuildConfig
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import androidx.compose.material.icons.filled.KeyboardVoice
import androidx.compose.material.icons.filled.VoiceOverOff
import androidx.compose.material.icons.filled.RecordVoiceOver
import androidx.compose.material.icons.filled.VoiceChat
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Logout
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.MicOff
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.AlertDialog

/**
 * Main screen - chat interface with WebSocket connection.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
    connectionService: com.openclaw.alfred.service.AlfredConnectionService?,
    serviceBound: Boolean,
    onLogout: () -> Unit,
    onAuthError: () -> Unit = {}
) {
    var connectionStatus by remember { mutableStateOf("Disconnected") }
    var messages by remember { mutableStateOf(emptyList<ChatMessage>()) }
    var inputText by remember { mutableStateOf("") }
    var gatewayClient: GatewayClient? by remember { mutableStateOf(null) }
    var isListening by remember { mutableStateOf(false) }
    var isSpeaking by remember { mutableStateOf(false) }
    var isProcessing by remember { mutableStateOf(false) }
    var processingStatus by remember { mutableStateOf("") }
    var ttsEnabled by remember { mutableStateOf(false) } // Default OFF
    var wakeWordEnabled by remember { mutableStateOf(false) } // Wake word mode OFF by default
    var conversationMode by remember { mutableStateOf(false) } // Continuous conversation mode OFF by default
    
    // UI state
    var showSettingsDialog by remember { mutableStateOf(false) }
    var showMenuDropdown by remember { mutableStateOf(false) }
    
    var voiceInputManager: VoiceInputManager? by remember { mutableStateOf(null) }
    var ttsManager: TTSManager? by remember { mutableStateOf(null) }
    var alarmManager: AlarmManager? by remember { mutableStateOf(null) }
    var wakeWordDetector: WakeWordDetector? by remember { mutableStateOf(null) }
    var wakeWordInitialized by remember { mutableStateOf(false) }
    var voskManager: VoskRecognitionManager? by remember { mutableStateOf(null) }
    var voskInitialized by remember { mutableStateOf(false) }
    var useVoskUnified by remember { mutableStateOf(false) } // Use old system (wake word + Google)
    var lastVoskConfidence by remember { mutableStateOf(1.0f) }
    var isAppInForeground by remember { mutableStateOf(true) }
    var showNotificationsSheet by remember { mutableStateOf(false) }
    var unreadNotificationCount by remember { mutableStateOf(0) }
    
    val context = LocalContext.current
    
    // Initialize ToneGenerator for wake word ready tone
    val toneGenerator = remember { 
        try {
            ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100)
        } catch (e: Exception) {
            Log.e("MainScreen", "Failed to create ToneGenerator: ${e.message}")
            null
        }
    }
    
    // Get assistant name from preferences (for display)
    val prefs = remember { context.getSharedPreferences("alfred_settings", android.content.Context.MODE_PRIVATE) }
    var assistantName by remember { 
        mutableStateOf(prefs.getString("assistant_name", "Alfred") ?: "Alfred") 
    }
    
    // Detect screen size for responsive layout
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp
    val isPhone = screenWidth < 600 // Typical phone vs tablet breakpoint
    
    // Responsive sizing
    val horizontalPadding: Dp = if (isPhone) 12.dp else 16.dp
    val buttonSize: Dp = if (isPhone) 52.dp else 56.dp
    val notificationStorage = remember { NotificationStorage(context) }
    val listState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()
    val lifecycleOwner = LocalLifecycleOwner.current
    
    // Permission launcher for microphone
    val micPermissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            // Permission granted, start listening
            voiceInputManager?.startListening()
        } else {
            // Permission denied
            messages = messages + ChatMessage("System", "Microphone permission denied", true)
        }
    }
    
    // Permission launcher for notifications (Android 13+)
    val notificationPermissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            Toast.makeText(context, "Notifications enabled", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(context, "Notifications disabled - you won't get background alerts", Toast.LENGTH_LONG).show()
        }
    }
    
    // Attachment state
    var attachmentUri by remember { mutableStateOf<android.net.Uri?>(null) }
    var attachmentName by remember { mutableStateOf<String?>(null) }
    
    // File picker launcher for attachments
    val attachmentLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri: android.net.Uri? ->
        if (uri != null) {
            attachmentUri = uri
            // Get filename from URI
            try {
                val cursor = context.contentResolver.query(uri, null, null, null, null)
                cursor?.use {
                    if (it.moveToFirst()) {
                        val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
                        if (nameIndex >= 0) {
                            attachmentName = it.getString(nameIndex)
                        }
                    }
                }
                if (attachmentName == null) {
                    attachmentName = uri.lastPathSegment ?: "attachment"
                }
                Toast.makeText(context, "Attached: $attachmentName", Toast.LENGTH_SHORT).show()
            } catch (e: Exception) {
                Log.e("MainScreen", "Failed to get filename", e)
                attachmentName = "attachment"
            }
        }
    }
    
    // Track app foreground/background state
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_RESUME -> {
                    isAppInForeground = true
                }
                Lifecycle.Event.ON_PAUSE -> {
                    isAppInForeground = false
                }
                else -> {}
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
    
    // Request notification permission on first launch (Android 13+)
    LaunchedEffect(Unit) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (!NotificationHelper.hasNotificationPermission(context)) {
                // Delay a bit so user isn't bombarded with permission requests
                kotlinx.coroutines.delay(2000)
                notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
            }
        }
    }
    
    // Load messages on first launch
    LaunchedEffect(Unit) {
        val loadedMessages = ConversationStorage.loadMessages(context)
        if (loadedMessages.isNotEmpty()) {
            messages = loadedMessages
        }
    }
    
    // Auto-scroll to bottom when messages change
    LaunchedEffect(messages.size) {
        if (messages.isNotEmpty()) {
            listState.animateScrollToItem(messages.size - 1)
        }
    }
    
    // Save messages whenever they change
    LaunchedEffect(messages) {
        if (messages.isNotEmpty()) {
            ConversationStorage.saveMessages(context, messages)
        }
    }
    
    // Helper function to send message with attachment
    fun sendMessageWithAttachment(text: String, attachment: android.net.Uri?, attName: String?) {
        val displayText = if (attachment != null && attName != null) {
            if (text.isNotBlank()) "$text [📎 $attName]" else "[📎 $attName]"
        } else {
            text
        }
        
        messages = messages + ChatMessage("You", displayText, false)
        
        // Send message with attachment
        if (attachment != null) {
            coroutineScope.launch(kotlinx.coroutines.Dispatchers.IO) {
                try {
                    // Upload file to proxy
                    val proxyUrl = BuildConfig.GATEWAY_URL.replace("wss://", "https://").replace("ws://", "http://")
                    val uploadUrl = "$proxyUrl/api/upload"
                    
                    val client = okhttp3.OkHttpClient()
                    
                    // Read file from content resolver
                    val inputStream = context.contentResolver.openInputStream(attachment)
                    val bytes = inputStream?.readBytes()
                    inputStream?.close()
                    
                    if (bytes == null) {
                        withContext(kotlinx.coroutines.Dispatchers.Main) {
                            messages = messages + ChatMessage("System", "Failed to read file", true)
                        }
                        gatewayClient?.sendMessage(text)
                        return@launch
                    }
                    
                    // Get MIME type
                    val mimeType = context.contentResolver.getType(attachment) ?: "application/octet-stream"
                    
                    // Create multipart request
                    val requestBody = okhttp3.MultipartBody.Builder()
                        .setType(okhttp3.MultipartBody.FORM)
                        .addFormDataPart(
                            "file",
                            attName ?: "file",
                            okhttp3.RequestBody.create(
                                mimeType.toMediaTypeOrNull(),
                                bytes
                            )
                        )
                        .build()
                    
                    val request = okhttp3.Request.Builder()
                        .url(uploadUrl)
                        .post(requestBody)
                        .build()
                    
                    Log.d("MainScreen", "Uploading file to: $uploadUrl")
                    Log.d("MainScreen", "File size: ${bytes.size} bytes")
                    
                    // Upload file
                    val response = client.newCall(request).execute()
                    
                    if (response.isSuccessful) {
                        val responseBody = response.body?.string()
                        Log.d("MainScreen", "Upload successful: $responseBody")
                        
                        // Parse response to get file path
                        val json = org.json.JSONObject(responseBody ?: "{}")
                        val filePath = json.optString("path", "")
                        
                        // Send message with file reference
                        val messageWithFile = buildString {
                            if (text.isNotBlank()) {
                                append(text)
                                append("\n\n")
                            }
                            append("Attached file: $attName\n")
                            append("File path: $filePath\n")
                            append("You can access the file at: $filePath")
                        }
                        
                        gatewayClient?.sendMessage(messageWithFile)
                    } else {
                        Log.e("MainScreen", "Upload failed: ${response.code} ${response.message}")
                        withContext(kotlinx.coroutines.Dispatchers.Main) {
                            messages = messages + ChatMessage("System", "Upload failed: ${response.code}", true)
                        }
                        gatewayClient?.sendMessage(text)
                    }
                    
                    response.close()
                } catch (e: Exception) {
                    Log.e("MainScreen", "Failed to upload file", e)
                    withContext(kotlinx.coroutines.Dispatchers.Main) {
                        messages = messages + ChatMessage("System", "Upload error: ${e.message}", true)
                    }
                    gatewayClient?.sendMessage(text)
                }
            }
        } else {
            gatewayClient?.sendMessage(text)
        }
        
        isProcessing = true
        processingStatus = "Processing..."
    }
    
    // Initialize voice input manager
    // IMPORTANT: Use connectionService as key so callbacks are recreated when service binds
    LaunchedEffect(connectionService) {
        voiceInputManager = VoiceInputManager(
            context = context,
            onResult = { text ->
                // Auto-send the message after voice input with any attachment
                if (text.isNotBlank() || attachmentUri != null) {
                    val attachment = attachmentUri
                    val attName = attachmentName
                    
                    // Clear attachment state
                    attachmentUri = null
                    attachmentName = null
                    
                    sendMessageWithAttachment(text, attachment, attName)
                }
                
                // In conversation mode, we'll restart after Alfred responds
                // Otherwise, restart wake word if enabled
                if (!conversationMode && wakeWordEnabled) {
                    Log.d("MainScreen", "Voice input complete, restarting wake word via service")
                    Handler(Looper.getMainLooper()).postDelayed({
                        val service = connectionService
                        if (service != null) {
                            Log.d("MainScreen", "Calling service.startWakeWord()")
                            service.startWakeWord()
                        } else {
                            Log.e("MainScreen", "connectionService is null, cannot restart wake word")
                        }
                    }, 500)
                }
            },
            onError = { error ->
                // Filter out "no speech detected" in conversation mode (normal timeout)
                if (!conversationMode || !error.contains("No speech detected", ignoreCase = true)) {
                    messages = messages + ChatMessage("System", "Voice error: $error", true)
                }
                
                // Restart wake word if enabled (not in conversation mode)
                if (!conversationMode && wakeWordEnabled) {
                    Log.d("MainScreen", "Voice error, restarting wake word via service")
                    Handler(Looper.getMainLooper()).postDelayed({
                        val service = connectionService
                        if (service != null) {
                            Log.d("MainScreen", "Calling service.startWakeWord() after error")
                            service.startWakeWord()
                        } else {
                            Log.e("MainScreen", "connectionService is null, cannot restart wake word")
                        }
                    }, 500)
                }
                // In conversation mode, we stop after timeout
            },
            onListening = { listening ->
                isListening = listening
            }
        )
        
        ttsManager = TTSManager(context)
        alarmManager = AlarmManager.getInstance(context)
        
        // Set up alarm dismiss callback for cross-device sync
        alarmManager?.onAlarmDismissed = { alarmId ->
            Log.d("MainScreen", "Alarm dismissed locally, broadcasting: $alarmId")
            gatewayClient?.dismissAlarm(alarmId)
        }
        
        if (useVoskUnified) {
            // Unified Vosk system (currently disabled)
            voskManager = VoskRecognitionManager(
                context = context,
                onWakeWordDetected = {
                    Toast.makeText(context, "Listening...", Toast.LENGTH_SHORT).show()
                    voskManager?.startFullRecognitionMode()
                },
                onTranscriptionResult = { text, confidence ->
                    lastVoskConfidence = confidence
                    isListening = false
                    
                    Log.d("MainScreen", "Vosk result: '$text' (confidence: $confidence)")
                    
                    if (confidence < 0.8f && text.split(" ").size > 3) {
                        messages = messages + ChatMessage("System", "Vosk (${(confidence * 100).toInt()}%): $text", true)
                        messages = messages + ChatMessage("System", "Low confidence - tap 'Retry with Google' if incorrect", true)
                        inputText = text
                        
                        if (wakeWordEnabled && voskInitialized) {
                            voskManager?.startWakeWordMode()
                        }
                    } else {
                        inputText = text
                        
                        if (text.isNotBlank()) {
                            messages = messages + ChatMessage("You", text, false)
                            gatewayClient?.sendMessage(text)
                            inputText = ""
                            isProcessing = true
                            processingStatus = "Processing..."
                        }
                        
                        if (wakeWordEnabled && voskInitialized) {
                            coroutineScope.launch {
                                kotlinx.coroutines.delay(500)
                                voskManager?.startWakeWordMode()
                            }
                        }
                    }
                },
                onError = { error ->
                    isListening = false
                    
                    if (!error.contains("No speech detected", ignoreCase = true)) {
                        messages = messages + ChatMessage("System", "Voice error: $error", true)
                    }
                    
                    if (wakeWordEnabled && voskInitialized) {
                        voskManager?.startWakeWordMode()
                    }
                },
                onInitialized = {
                    voskInitialized = true
                    Toast.makeText(context, "Voice recognition ready!", Toast.LENGTH_SHORT).show()
                }
            )
            
            coroutineScope.launch {
                Toast.makeText(context, "Loading voice recognition...", Toast.LENGTH_SHORT).show()
                voskManager?.initialize()
            }
        } else {
            // OLD SYSTEM: Vosk wake word + Google transcription (ACTIVE)
            // Wake word now managed by AlfredConnectionService for continuous listening
            Log.d("MainScreen", "Wake word will be managed by service")
        }
    }
    
    // Track if listener has been set up to prevent duplicates
    var listenerSetUp by remember { mutableStateOf(false) }
    
    // Initialize gateway client from service (re-runs when service binding changes)
    LaunchedEffect(serviceBound, connectionService) {
        if (!serviceBound || connectionService == null) {
            Log.d("MainScreen", "Service not bound yet, waiting...")
            connectionStatus = "Starting..."
            return@LaunchedEffect
        }
        
        if (listenerSetUp) {
            Log.d("MainScreen", "Listener already set up, skipping duplicate setup")
            return@LaunchedEffect
        }
        
        Log.d("MainScreen", "Service bound, setting up listener")
        listenerSetUp = true
        gatewayClient = connectionService.getGatewayClient()
        
        val listener = object : GatewayListener {
            override fun onConnecting() {
                connectionStatus = "Connecting..."
            }
            
            override fun onConnected() {
                connectionStatus = "Connected ✅"
                // Don't show system message - keep chat clean
                
                // Send FCM token to proxy for push notifications
                // Always send on connect so proxy can re-register after restarts
                val prefs = context.getSharedPreferences("alfred_prefs", android.content.Context.MODE_PRIVATE)
                val fcmToken = prefs.getString("fcm_token", null)
                
                if (fcmToken != null) {
                    Log.d("MainScreen", "Sending FCM token to proxy on connect")
                    gatewayClient?.sendFCMToken(fcmToken)
                } else {
                    Log.w("MainScreen", "No FCM token available yet")
                }
            }
            
            override fun onDisconnected() {
                connectionStatus = "Disconnected"
                messages = messages + ChatMessage("System", "Disconnected from gateway", true)
                isProcessing = false
                processingStatus = ""
            }
            
            override fun onReconnecting(attempt: Int, delayMs: Long) {
                val delaySec = delayMs / 1000
                connectionStatus = "Reconnecting... (attempt $attempt, ${delaySec}s)"
                Log.d("MainScreen", "Reconnecting: attempt $attempt, delay ${delayMs}ms")
            }
            
            override fun onError(error: String) {
                connectionStatus = "Error: $error"
                
                // Check for authentication errors (401, invalid token, etc.)
                if (error.contains("401") || 
                    error.contains("Authentication") || 
                    error.contains("Invalid token") ||
                    error.contains("Unauthorized")) {
                    Log.w("MainScreen", "Authentication error detected, triggering token refresh")
                    messages = messages + ChatMessage("System", "⚠️ Session expired. Refreshing...", true)
                    onAuthError()
                    return
                }
                
                // Only show error in chat if it's not a reconnection-related error
                if (!error.contains("max retries")) {
                    messages = messages + ChatMessage("System", "Error: $error", true)
                } else {
                    // Max retries error - show in chat
                    messages = messages + ChatMessage("System", "⚠️ $error. Please check your connection and restart the app.", true)
                }
                isProcessing = false
                processingStatus = ""
            }
            
            override fun onEvent(event: String, payload: String) {
                // Handle agent lifecycle events for processing indicator
                if (event == "agent") {
                    try {
                        val json = org.json.JSONObject(payload)
                        val stream = json.optString("stream", "")
                        val data = json.optJSONObject("data")
                        
                        when (stream) {
                            "lifecycle" -> {
                                val phase = data?.optString("phase", "")
                                when (phase) {
                                    "start" -> {
                                        isProcessing = true
                                        processingStatus = "Thinking..."
                                    }
                                    "end" -> {
                                        isProcessing = false
                                        processingStatus = ""
                                    }
                                }
                            }
                            "tool" -> {
                                val toolName = data?.optString("tool", "") ?: ""
                                if (toolName.isNotEmpty()) {
                                    isProcessing = true
                                    processingStatus = "Using tool: $toolName"
                                }
                            }
                        }
                    } catch (e: Exception) {
                        // Ignore parsing errors
                    }
                }
            }
            
            override fun onResponse(id: String, payload: String) {
                // Ignore raw responses
            }
            
            override fun onMessage(sender: String, text: String) {
                // Filter out NO_REPLY internal messages
                if (text.trim().equals("NO_REPLY", ignoreCase = true)) {
                    Log.d("MainScreen", "Filtered NO_REPLY message")
                    return
                }
                
                // Use custom assistant name if sender is "Alfred"
                val displaySender = if (sender == "Alfred") assistantName else sender
                messages = messages + ChatMessage(displaySender, text, false)
                
                // Clear processing indicator when we get Alfred's response
                if (sender == "Alfred") {
                    isProcessing = false
                    processingStatus = ""
                }
                
                // Send notification if app is in background
                if (!isAppInForeground && sender == "Alfred") {
                    NotificationHelper.showBackgroundWorkComplete(
                        context = context,
                        message = text
                    )
                }
                
                // Auto-play TTS for Alfred's messages (if enabled and in foreground)
                if (sender == "Alfred" && ttsEnabled && isAppInForeground) {
                    isSpeaking = true
                    ttsManager?.speak(
                        text = text,
                        onComplete = {
                            isSpeaking = false
                            
                            // In conversation mode, restart voice input after TTS finishes
                            if (conversationMode && isAppInForeground) {
                                coroutineScope.launch {
                                    kotlinx.coroutines.delay(500) // Brief pause
                                    if (PermissionHelper.hasMicrophonePermission(context)) {
                                        voiceInputManager?.startListening()
                                    }
                                }
                            }
                        },
                        onError = { error ->
                            isSpeaking = false
                            messages = messages + ChatMessage("System", "TTS error: $error", true)
                        }
                    )
                } else if (sender == "Alfred" && conversationMode && isAppInForeground) {
                    // Conversation mode without TTS - restart voice immediately
                    coroutineScope.launch {
                        kotlinx.coroutines.delay(500) // Brief pause
                        if (PermissionHelper.hasMicrophonePermission(context)) {
                            voiceInputManager?.startListening()
                        }
                    }
                }
            }
            
            override fun onNotification(
                notificationType: String,
                title: String,
                message: String,
                priority: String,
                sound: Boolean,
                vibrate: Boolean,
                timestamp: Long,
                action: String?
            ) {
                Log.d("MainScreen", "Received notification: type=$notificationType title=$title message=$message")
                
                // Save notification to history
                notificationStorage.addNotification(notificationType, title, message, timestamp)
                unreadNotificationCount = notificationStorage.getUnreadCount()
                
                // Add notification icon based on type
                val icon = when (notificationType) {
                    "alarm" -> "🔔"
                    "timer" -> "⏰"
                    "reminder" -> "🔔"
                    "alert" -> "⚠️"
                    else -> "📢"
                }
                
                // Handle alarms with repeating sound
                if (notificationType == "alarm") {
                    // Create stable alarm ID from message content so duplicates are recognized
                    val alarmId = "alarm-${title.hashCode()}-${message.hashCode()}"
                    
                    // Check if this alarm is already active (deduplicate WebSocket + FCM)
                    if (alarmManager?.getActiveAlarms()?.any { it.id == alarmId } == true) {
                        Log.d("MainScreen", "Alarm $alarmId already active, skipping duplicate")
                        return
                    }
                    
                    // Start alarm sound/vibration
                    alarmManager?.startAlarm(
                        alarmId = alarmId,
                        title = title,
                        message = message,
                        enableSound = sound,
                        enableVibrate = vibrate
                    )
                    
                    // Create intent for AlarmActivity (full-screen)
                    val alarmIntent = Intent(context, AlarmActivity::class.java).apply {
                        putExtra(AlarmActivity.EXTRA_ALARM_ID, alarmId)
                        putExtra(AlarmActivity.EXTRA_TITLE, title)
                        putExtra(AlarmActivity.EXTRA_MESSAGE, message)
                        putExtra(AlarmActivity.EXTRA_TIMESTAMP, timestamp)
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }
                    
                    val fullScreenPendingIntent = PendingIntent.getActivity(
                        context,
                        alarmId.hashCode(),
                        alarmIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                    )
                    
                    // Create dismiss action intent (broadcast to dismiss the alarm)
                    val dismissIntent = Intent("com.openclaw.alfred.DISMISS_ALARM").apply {
                        putExtra("alarm_id", alarmId)
                        setPackage(context.packageName)
                    }
                    
                    val dismissPendingIntent = PendingIntent.getBroadcast(
                        context,
                        (alarmId.hashCode() + 1),
                        dismissIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                    )
                    
                    // Show alarm notification with full-screen intent and dismiss button
                    NotificationHelper.showAlarmNotification(
                        context = context,
                        alarmId = alarmId,
                        title = title,
                        message = message,
                        fullScreenIntent = fullScreenPendingIntent,
                        dismissAction = dismissPendingIntent
                    )
                    
                    // Launch AlarmActivity immediately
                    context.startActivity(alarmIntent)
                    
                    // Show in chat
                    messages = messages + ChatMessage(title, "🔔 ALARM: $message", true)
                    
                    // Auto-scroll to latest message
                    coroutineScope.launch {
                        listState.animateScrollToItem(messages.size - 1)
                    }
                    
                    return
                }
                
                // Always show system notification for timers/reminders (even in foreground)
                if (notificationType == "timer" || notificationType == "reminder") {
                    NotificationHelper.showNotification(
                        context = context,
                        title = title,
                        message = "$icon $message",
                        autoCancel = true
                    )
                }
                
                // Show in chat if in foreground
                if (isAppInForeground) {
                    messages = messages + ChatMessage(title, "$icon $message", true)
                    
                    // Auto-scroll to latest message
                    coroutineScope.launch {
                        listState.animateScrollToItem(messages.size - 1)
                    }
                    
                    // Optional TTS for reminders/timers (if TTS enabled)
                    if (ttsEnabled && (notificationType == "timer" || notificationType == "reminder")) {
                        ttsManager?.speak(
                            text = message,
                            onComplete = {},
                            onError = {}
                        )
                    }
                } else {
                    // Show system notification if in background
                    NotificationHelper.showNotification(
                        context = context,
                        title = title,
                        message = "$icon $message",
                        autoCancel = true
                    )
                }
            }
            
            override fun onAlarmDismissed(alarmId: String) {
                Log.d("MainScreen", "Received alarm dismiss broadcast: $alarmId")
                
                // Dismiss the alarm locally (without triggering another broadcast)
                alarmManager?.let { manager ->
                    // Temporarily clear the callback to avoid re-broadcasting
                    val callback = manager.onAlarmDismissed
                    manager.onAlarmDismissed = null
                    manager.dismissAlarm(alarmId)
                    manager.onAlarmDismissed = callback
                }
            }
            
            override fun onWakeWordDetected() {
                Log.d("MainScreen", "Wake word detected (from service)")
                
                Toast.makeText(context, "Listening...", Toast.LENGTH_SHORT).show()
                coroutineScope.launch {
                    // Wait 200ms for wake word detector to fully release mic
                    kotlinx.coroutines.delay(200)
                    
                    // Play ready tone if enabled in settings
                    val toneEnabled = prefs.getBoolean("wake_word_tone", false)
                    if (toneEnabled) {
                        try {
                            toneGenerator?.startTone(ToneGenerator.TONE_PROP_BEEP, 150) // Short beep
                        } catch (e: Exception) {
                            Log.e("MainScreen", "Failed to play tone: ${e.message}")
                        }
                        // Wait for tone to finish + extra delay
                        kotlinx.coroutines.delay(1300)
                    } else {
                        // No tone, shorter delay
                        kotlinx.coroutines.delay(300)
                    }
                    
                    Log.d("MainScreen", "Starting voice input after delay")
                    if (PermissionHelper.hasMicrophonePermission(context)) {
                        voiceInputManager?.startListening()
                    } else {
                        Log.w("MainScreen", "No microphone permission")
                    }
                }
            }
        }
        
        // Set listener on the service
        Log.d("MainScreen", "About to set listener on service")
        connectionService.setListener(listener)
        Log.d("MainScreen", "Listener set on service")
        
        // Get reference to gateway client for sending messages
        gatewayClient = connectionService.getGatewayClient()
        Log.d("MainScreen", "Got gateway client reference: ${gatewayClient != null}")
        
        // Get current connection state (service may already be connected)
        val isConnected = gatewayClient?.isConnected() ?: false
        Log.d("MainScreen", "Gateway isConnected: $isConnected")
        
        if (isConnected) {
            Log.d("MainScreen", "Gateway already connected, updating UI status")
            connectionStatus = "Connected ✅"
            
            // Send FCM token if we have one
            val prefs = context.getSharedPreferences("alfred_prefs", android.content.Context.MODE_PRIVATE)
            val fcmToken = prefs.getString("fcm_token", null)
            if (fcmToken != null) {
                Log.d("MainScreen", "Sending FCM token to proxy (already connected)")
                gatewayClient?.sendFCMToken(fcmToken)
            }
        } else {
            Log.d("MainScreen", "Gateway not connected yet, waiting for onConnected callback")
            connectionStatus = "Connecting..."
        }
        
        // Note: We don't clear the listener on dispose because the service continues
        // running in the background. The listener will be replaced when we reconnect
        // or cleared when the service is stopped (on logout).
    }
    
    // Get FCM token for push notifications
    LaunchedEffect(Unit) {
        try {
            com.google.firebase.messaging.FirebaseMessaging.getInstance().token
                .addOnCompleteListener { task ->
                    if (task.isSuccessful) {
                        val token = task.result
                        Log.d("MainScreen", "FCM token retrieved: ${token.take(20)}...")
                        
                        // Store token
                        val prefs = context.getSharedPreferences("alfred_prefs", android.content.Context.MODE_PRIVATE)
                        prefs.edit()
                            .putString("fcm_token", token)
                            .apply()
                        
                        // Send to proxy if already connected
                        if (gatewayClient != null) {
                            Log.d("MainScreen", "Sending newly obtained FCM token to proxy")
                            gatewayClient?.sendFCMToken(token)
                        }
                    } else {
                        Log.e("MainScreen", "Failed to get FCM token", task.exception)
                    }
                }
        } catch (e: Exception) {
            Log.e("MainScreen", "Error getting FCM token", e)
        }
    }
    
    // Cleanup on dispose
    DisposableEffect(Unit) {
        onDispose {
            // Don't disconnect gateway - service manages connection lifecycle
            voiceInputManager?.destroy()
            ttsManager?.destroy()
            alarmManager?.destroy()
            wakeWordDetector?.destroy()
            voskManager?.destroy()
            toneGenerator?.release()
        }
    }
    
    // Handle wake word mode toggle - delegate to service for continuous listening
    LaunchedEffect(wakeWordEnabled, serviceBound) {
        Log.d("MainScreen", "Wake word effect: enabled=$wakeWordEnabled, serviceBound=$serviceBound")
        
        if (useVoskUnified) {
            if (wakeWordEnabled) {
                if (voskInitialized) {
                    if (PermissionHelper.hasMicrophonePermission(context)) {
                        voskManager?.startWakeWordMode()
                    } else {
                        micPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
                        wakeWordEnabled = false
                    }
                } else {
                    messages = messages + ChatMessage("System", "Voice recognition still loading...", true)
                    wakeWordEnabled = false
                }
            } else {
                voskManager?.stop()
            }
        } else {
            // Wake word managed by service for continuous listening
            val service = connectionService
            if (service != null && serviceBound) {
                if (wakeWordEnabled) {
                    Log.d("MainScreen", "Requesting service to start wake word")
                    if (PermissionHelper.hasMicrophonePermission(context)) {
                        service.startWakeWord()
                        Toast.makeText(context, "Loading wake word model...", Toast.LENGTH_SHORT).show()
                    } else {
                        Log.d("MainScreen", "Requesting microphone permission for wake word")
                        micPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
                        wakeWordEnabled = false
                    }
                } else {
                    Log.d("MainScreen", "Requesting service to stop wake word")
                    service.stopWakeWord()
                }
            } else {
                Log.w("MainScreen", "Service not available for wake word control")
            }
        }
    }
    
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            // Top bar
            TopAppBar(
                title = { 
                    Text(
                        text = "AI Assistant",
                        style = MaterialTheme.typography.titleMedium
                    )
                },
                actions = {
                    // Notifications button with badge
                    BadgedBox(
                        badge = {
                            if (unreadNotificationCount > 0) {
                                Badge {
                                    Text(unreadNotificationCount.toString())
                                }
                            }
                        }
                    ) {
                        IconButton(onClick = {
                            showNotificationsSheet = true
                        }) {
                            Icon(
                                imageVector = Icons.Default.Notifications,
                                contentDescription = "Notifications"
                            )
                        }
                    }
                    
                    TextButton(onClick = {
                        messages = emptyList()
                        ConversationStorage.clearMessages(context)
                        Toast.makeText(context, "Conversation cleared", Toast.LENGTH_SHORT).show()
                    }) {
                        Text("Clear")
                    }
                    
                    // Menu dropdown for Settings and Logout
                    Box {
                        IconButton(onClick = { showMenuDropdown = true }) {
                            Icon(
                                imageVector = Icons.Default.MoreVert,
                                contentDescription = "Menu"
                            )
                        }
                        
                        DropdownMenu(
                            expanded = showMenuDropdown,
                            onDismissRequest = { showMenuDropdown = false }
                        ) {
                            DropdownMenuItem(
                                text = { Text("Settings") },
                                onClick = {
                                    showMenuDropdown = false
                                    showSettingsDialog = true
                                },
                                leadingIcon = {
                                    Icon(Icons.Default.Settings, contentDescription = null)
                                }
                            )
                            DropdownMenuItem(
                                text = { Text("Logout") },
                                onClick = {
                                    showMenuDropdown = false
                                    onLogout()
                                },
                                leadingIcon = {
                                    Icon(Icons.Default.Logout, contentDescription = null)
                                }
                            )
                        }
                    }
                }
            )
            
            // Connection status with TTS toggle
            Surface(
                modifier = Modifier.fillMaxWidth(),
                color = MaterialTheme.colorScheme.surfaceVariant
            ) {
                Row(
                    modifier = Modifier.fillMaxWidth().padding(8.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    // Left side: status indicators
                    Row(
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        // Show processing status prominently if active
                        if (isProcessing) {
                            Text(
                                text = "⏳ $processingStatus",
                                style = MaterialTheme.typography.bodySmall,
                                color = MaterialTheme.colorScheme.tertiary
                            )
                        } else {
                            Text(
                                text = connectionStatus,
                                style = MaterialTheme.typography.bodySmall
                            )
                        }
                        
                        if (isSpeaking) {
                            Spacer(modifier = Modifier.width(12.dp))
                            Text(
                                text = "🔊 Speaking...",
                                style = MaterialTheme.typography.bodySmall,
                                color = MaterialTheme.colorScheme.primary
                            )
                        }
                        
                        if (isListening) {
                            Spacer(modifier = Modifier.width(12.dp))
                            Text(
                                text = "🎤 Listening...",
                                style = MaterialTheme.typography.bodySmall,
                                color = MaterialTheme.colorScheme.error
                            )
                        }
                    }
                    
                    // Right side: toggle chips (scrollable if too many)
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(6.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        // Conversation mode toggle
                        FilterChip(
                            selected = conversationMode,
                            onClick = {
                                conversationMode = !conversationMode
                                // Turn off wake word when entering conversation mode
                                if (conversationMode && wakeWordEnabled) {
                                    wakeWordEnabled = false
                                }
                            },
                            label = {
                                Text(
                                    text = if (conversationMode) "Chat On" else "Chat Mode",
                                    style = MaterialTheme.typography.labelSmall
                                )
                            },
                            leadingIcon = {
                                Icon(
                                    imageVector = if (conversationMode) Icons.Default.RecordVoiceOver else Icons.Default.VoiceChat,
                                    contentDescription = null,
                                    modifier = Modifier.size(16.dp)
                                )
                            }
                        )
                        
                        // Wake word toggle moved to Settings
                        
                        // TTS toggle
                        FilterChip(
                            selected = ttsEnabled,
                            onClick = {
                                ttsEnabled = !ttsEnabled
                                if (!ttsEnabled) {
                                    ttsManager?.stopPlayback()
                                    isSpeaking = false
                                }
                            },
                            label = {
                                Text(
                                    text = if (ttsEnabled) "Voice On" else "Voice Off",
                                    style = MaterialTheme.typography.labelSmall
                                )
                            },
                            leadingIcon = {
                                Icon(
                                    imageVector = if (ttsEnabled) Icons.Default.VolumeUp else Icons.Default.VolumeOff,
                                    contentDescription = null,
                                    modifier = Modifier.size(16.dp)
                                )
                            }
                        )
                    }
                }
            }
            
            // Message list
            LazyColumn(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp),
                state = listState
            ) {
                items(messages) { message ->
                    com.openclaw.alfred.ui.components.RichMessageBubble(message)
                    Spacer(modifier = Modifier.height(8.dp))
                }
            }
            
            // Input area
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = horizontalPadding, vertical = 12.dp),
                verticalAlignment = Alignment.Bottom
            ) {
                // Voice input button (also stops TTS if speaking)
                FilledIconButton(
                    modifier = Modifier.size(buttonSize),
                    onClick = {
                        // If TTS is speaking, stop it
                        if (isSpeaking) {
                            ttsManager?.stopPlayback()
                            isSpeaking = false
                            return@FilledIconButton
                        }
                        
                        if (useVoskUnified) {
                            // Unified Vosk system
                            if (isListening) {
                                voskManager?.stop()
                                isListening = false
                                
                                // Restart wake word mode if it was enabled
                                if (wakeWordEnabled && voskInitialized) {
                                    voskManager?.startWakeWordMode()
                                }
                            } else {
                                // Check permission first
                                if (PermissionHelper.hasMicrophonePermission(context)) {
                                    voskManager?.startFullRecognitionMode()
                                    isListening = true
                                } else {
                                    micPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
                                }
                            }
                        } else {
                            // Legacy system
                            if (isListening) {
                                voiceInputManager?.stopListening()
                                
                                // Restart wake word detector if it was enabled
                                if (wakeWordEnabled) {
                                    Log.d("MainScreen", "Manually stopped listening, restarting wake word via service")
                                    Handler(Looper.getMainLooper()).postDelayed({
                                        val service = connectionService
                                        if (service != null) {
                                            Log.d("MainScreen", "Calling service.startWakeWord() after manual stop")
                                            service.startWakeWord()
                                        } else {
                                            Log.e("MainScreen", "connectionService is null, cannot restart wake word")
                                        }
                                    }, 500)
                                }
                            } else {
                                // Stop wake word detector temporarily when manually starting voice
                                if (wakeWordEnabled) {
                                    connectionService?.stopWakeWord()
                                }
                                
                                // Check permission first
                                if (PermissionHelper.hasMicrophonePermission(context)) {
                                    voiceInputManager?.startListening()
                                } else {
                                    micPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
                                }
                            }
                        }
                    },
                    colors = IconButtonDefaults.filledIconButtonColors(
                        containerColor = if (isListening || isSpeaking) 
                            MaterialTheme.colorScheme.error 
                        else 
                            MaterialTheme.colorScheme.secondaryContainer
                    )
                ) {
                    Icon(
                        if (isListening || isSpeaking) Icons.Default.Stop else Icons.Default.Mic,
                        contentDescription = when {
                            isSpeaking -> "Stop speaking"
                            isListening -> "Stop listening"
                            else -> "Voice input"
                        }
                    )
                }
                
                Spacer(modifier = Modifier.width(8.dp))
                
                // Attachment button
                FilledTonalIconButton(
                    onClick = {
                        // Launch file picker
                        attachmentLauncher.launch("*/*")
                    },
                    modifier = Modifier.size(buttonSize)
                ) {
                    Icon(
                        imageVector = androidx.compose.material.icons.Icons.Default.AttachFile,
                        contentDescription = "Attach file"
                    )
                }
                
                Spacer(modifier = Modifier.width(8.dp))
                
                Column(modifier = Modifier.weight(1f)) {
                    OutlinedTextField(
                        value = inputText,
                        onValueChange = { inputText = it },
                        modifier = Modifier.fillMaxWidth(),
                        placeholder = { Text("Message $assistantName...") },
                        maxLines = 3
                    )
                    
                    // Show attachment preview
                    if (attachmentUri != null && attachmentName != null) {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(top = 4.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Icon(
                                imageVector = androidx.compose.material.icons.Icons.Default.AttachFile,
                                contentDescription = null,
                                modifier = Modifier.size(16.dp),
                                tint = MaterialTheme.colorScheme.primary
                            )
                            Spacer(modifier = Modifier.width(4.dp))
                            Text(
                                text = attachmentName ?: "",
                                style = MaterialTheme.typography.bodySmall,
                                color = MaterialTheme.colorScheme.primary,
                                modifier = Modifier.weight(1f)
                            )
                            IconButton(
                                onClick = {
                                    attachmentUri = null
                                    attachmentName = null
                                },
                                modifier = Modifier.size(20.dp)
                            ) {
                                Icon(
                                    imageVector = androidx.compose.material.icons.Icons.Default.Close,
                                    contentDescription = "Remove attachment",
                                    modifier = Modifier.size(16.dp)
                                )
                            }
                        }
                    }
                }
                
                Spacer(modifier = Modifier.width(8.dp))
                
                FilledIconButton(
                    onClick = {
                        if (inputText.isNotBlank() || attachmentUri != null) {
                            val text = inputText
                            val attachment = attachmentUri
                            val attName = attachmentName
                            
                            inputText = ""
                            attachmentUri = null
                            attachmentName = null
                            
                            // Check for alarm dismissal commands
                            if (text.contains("dismiss", ignoreCase = true) && 
                                (text.contains("alarm", ignoreCase = true) || text.contains("alert", ignoreCase = true))) {
                                alarmManager?.dismissAll()
                                messages = messages + ChatMessage("You", text, false)
                                messages = messages + ChatMessage(assistantName, "✅ Alarm dismissed", false)
                                // Auto-scroll
                                coroutineScope.launch {
                                    listState.animateScrollToItem(messages.size - 1)
                                }
                            } else {
                                // Use helper function to send with attachment
                                sendMessageWithAttachment(text, attachment, attName)
                            }
                        }
                    },
                    modifier = Modifier.size(buttonSize),
                    enabled = inputText.isNotBlank() || attachmentUri != null
                ) {
                    Icon(Icons.Default.Send, contentDescription = "Send")
                }
            }
        }
    }
    
    // Notifications dialog
    if (showNotificationsSheet) {
        Dialog(
            onDismissRequest = {
                showNotificationsSheet = false
                // Mark all as read when dialog is closed
                notificationStorage.markAllAsRead()
                unreadNotificationCount = 0
            }
        ) {
            Surface(
                modifier = Modifier
                    .fillMaxWidth(if (isPhone) 0.98f else 0.9f)
                    .fillMaxHeight(if (isPhone) 0.9f else 0.8f),
                shape = MaterialTheme.shapes.large,
                color = MaterialTheme.colorScheme.surface
            ) {
                NotificationHistorySheet(
                    notificationStorage = notificationStorage,
                    horizontalPadding = horizontalPadding,
                    onClose = {
                        showNotificationsSheet = false
                        notificationStorage.markAllAsRead()
                        unreadNotificationCount = 0
                    }
                )
            }
        }
    }
    
    // Settings Dialog
    if (showSettingsDialog) {
        SettingsDialog(
            wakeWordEnabled = wakeWordEnabled,
            conversationMode = conversationMode,
            onWakeWordToggle = { enabled ->
                wakeWordEnabled = enabled
                if (wakeWordEnabled && conversationMode) {
                    conversationMode = false
                }
            },
            onDismiss = { showSettingsDialog = false }
        )
    }
    
    // Update unread count on app resume
    LaunchedEffect(isAppInForeground) {
        if (isAppInForeground) {
            unreadNotificationCount = notificationStorage.getUnreadCount()
        }
    }
}

@Composable
private fun NotificationHistorySheet(
    notificationStorage: NotificationStorage,
    horizontalPadding: Dp,
    onClose: () -> Unit
) {
    val context = LocalContext.current
    var notifications by remember { mutableStateOf(notificationStorage.getNotifications()) }
    
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = horizontalPadding, vertical = 12.dp)
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Notifications",
                style = MaterialTheme.typography.headlineSmall
            )
            Row {
                TextButton(onClick = {
                    notificationStorage.clearAll()
                    onClose()
                    Toast.makeText(
                        context,
                        "Notifications cleared",
                        Toast.LENGTH_SHORT
                    ).show()
                }) {
                    Text("Clear All")
                }
                IconButton(onClick = onClose) {
                    Icon(Icons.Default.Close, contentDescription = "Close")
                }
            }
        }
        
        Spacer(modifier = Modifier.height(8.dp))
        
        if (notifications.isEmpty()) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(32.dp),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "No notifications",
                    style = MaterialTheme.typography.bodyLarge,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        } else {
            LazyColumn {
                items(notifications, key = { it.timestamp }) { notification ->
                    SwipeToDeleteNotificationItem(
                        notification = notification,
                        onDelete = {
                            notificationStorage.deleteNotification(notification.timestamp)
                            notifications = notificationStorage.getNotifications()
                            Toast.makeText(
                                context,
                                "Notification dismissed",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    )
                    Divider()
                }
            }
        }
        
        Spacer(modifier = Modifier.height(16.dp))
    }
}

@Composable
private fun SwipeToDeleteNotificationItem(
    notification: NotificationStorage.StoredNotification,
    onDelete: () -> Unit
) {
    var offsetX by remember { mutableStateOf(0f) }
    var shouldDelete by remember { mutableStateOf(false) }
    val dismissThreshold = 300f
    
    // Handle deletion in a separate effect to avoid state updates during gesture
    LaunchedEffect(shouldDelete) {
        if (shouldDelete) {
            kotlinx.coroutines.delay(100) // Small delay for animation
            onDelete()
            shouldDelete = false
        }
    }
    
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .pointerInput(notification.timestamp) { // Use notification id as key
                detectHorizontalDragGestures(
                    onDragEnd = {
                        if (abs(offsetX) > dismissThreshold) {
                            // Trigger deletion via state flag, not direct call
                            shouldDelete = true
                            offsetX = 0f
                        } else {
                            offsetX = 0f
                        }
                    },
                    onHorizontalDrag = { _, dragAmount ->
                        offsetX += dragAmount
                    }
                )
            }
    ) {
        // Delete icon background
        if (abs(offsetX) > 50f) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 12.dp),
                horizontalArrangement = if (offsetX > 0) Arrangement.Start else Arrangement.End,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Icon(
                    imageVector = Icons.Default.Delete,
                    contentDescription = "Delete",
                    tint = MaterialTheme.colorScheme.error,
                    modifier = Modifier.padding(horizontal = 16.dp)
                )
            }
        }
        
        // Notification content
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .graphicsLayer {
                    translationX = offsetX
                }
        ) {
            Surface(
                color = MaterialTheme.colorScheme.surface
            ) {
                NotificationItem(notification)
            }
        }
    }
}

@Composable
private fun NotificationItem(notification: NotificationStorage.StoredNotification) {
    val icon = when (notification.type) {
        "alarm" -> "🔔"
        "timer" -> "⏰"
        "reminder" -> "🔔"
        "alert" -> "⚠️"
        else -> "📢"
    }
    
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 12.dp)
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Text(
                text = "$icon ${notification.title}",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.weight(1f)
            )
            Text(
                text = formatTimestamp(notification.timestamp),
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }
        Spacer(modifier = Modifier.height(4.dp))
        Text(
            text = notification.message,
            style = MaterialTheme.typography.bodyMedium
        )
    }
}

private fun formatTimestamp(timestamp: Long): String {
    val now = System.currentTimeMillis()
    val diff = now - timestamp
    
    return when {
        diff < 60_000 -> "Just now"
        diff < 3600_000 -> "${diff / 60_000}m ago"
        diff < 86400_000 -> "${diff / 3600_000}h ago"
        else -> "${diff / 86400_000}d ago"
    }
}

@Composable
private fun ChatMessageBubble(message: ChatMessage) {
    val alignment = if (message.sender == "You") Alignment.End else Alignment.Start
    val color = when {
        message.isSystem -> MaterialTheme.colorScheme.surfaceVariant
        message.sender == "You" -> MaterialTheme.colorScheme.primaryContainer
        else -> MaterialTheme.colorScheme.secondaryContainer
    }
    
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = alignment
    ) {
        SelectionContainer {
            Card(
                colors = CardDefaults.cardColors(containerColor = color)
            ) {
                Column(
                    modifier = Modifier.padding(12.dp)
                ) {
                    if (message.sender != "You") {
                        Text(
                            text = message.sender,
                            style = MaterialTheme.typography.labelSmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                        Spacer(modifier = Modifier.height(4.dp))
                    }
                    Text(
                        text = message.text,
                        style = MaterialTheme.typography.bodyMedium
                    )
                }
            }
        }
    }
}

data class ChatMessage(
    val sender: String,
    val text: String,
    val isSystem: Boolean = false
)

@Composable
private fun SettingsDialog(
    wakeWordEnabled: Boolean,
    conversationMode: Boolean,
    onWakeWordToggle: (Boolean) -> Unit,
    onDismiss: () -> Unit
) {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val prefs = context.getSharedPreferences("alfred_settings", android.content.Context.MODE_PRIVATE)
    
    // State
    var voices by remember { mutableStateOf<List<com.openclaw.alfred.voice.VoiceHelper.Voice>>(emptyList()) }
    var selectedVoiceId by remember { mutableStateOf(prefs.getString("tts_voice_id", "vBKc2FfBKJfcZNyEt1n6") ?: "vBKc2FfBKJfcZNyEt1n6") }
    var customWakeWord by remember { mutableStateOf(prefs.getString("wake_word", "alfred") ?: "alfred") }
    var isLoadingVoices by remember { mutableStateOf(false) }
    var showVoiceDropdown by remember { mutableStateOf(false) }
    var isTesting by remember { mutableStateOf(false) }
    var alarmSoundUri by remember { mutableStateOf(prefs.getString("alarm_sound_uri", null)) }
    var alarmSoundName by remember { 
        mutableStateOf(
            if (alarmSoundUri != null) {
                try {
                    val uri = android.net.Uri.parse(alarmSoundUri)
                    val ringtone = android.media.RingtoneManager.getRingtone(context, uri)
                    ringtone?.getTitle(context) ?: "Custom Sound"
                } catch (e: Exception) {
                    "Default Alarm"
                }
            } else {
                "Default Alarm"
            }
        )
    }
    
    // Alarm sound picker launcher
    val alarmSoundLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == android.app.Activity.RESULT_OK) {
            val uri = result.data?.getParcelableExtra<android.net.Uri>(android.media.RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
            if (uri != null) {
                alarmSoundUri = uri.toString()
                prefs.edit().putString("alarm_sound_uri", alarmSoundUri).apply()
                
                // Update display name
                try {
                    val ringtone = android.media.RingtoneManager.getRingtone(context, uri)
                    alarmSoundName = ringtone?.getTitle(context) ?: "Custom Sound"
                } catch (e: Exception) {
                    alarmSoundName = "Custom Sound"
                }
                
                Toast.makeText(context, "Alarm sound updated", Toast.LENGTH_SHORT).show()
            }
        }
    }
    
    // Load voices on first display
    LaunchedEffect(Unit) {
        isLoadingVoices = true
        voices = com.openclaw.alfred.voice.VoiceHelper.fetchVoices()
        isLoadingVoices = false
    }
    
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Settings") },
        text = {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .verticalScroll(rememberScrollState())
            ) {
                // Wake Word Toggle
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 8.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Column(modifier = Modifier.weight(1f)) {
                        Text(
                            text = "Wake Word Mode",
                            style = MaterialTheme.typography.bodyLarge
                        )
                        Text(
                            text = if (wakeWordEnabled) "Always listening" else "Tap to talk",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                    }
                    Switch(
                        checked = wakeWordEnabled,
                        onCheckedChange = onWakeWordToggle,
                        enabled = !conversationMode
                    )
                }
                
                if (conversationMode) {
                    Text(
                        text = "Wake word disabled in conversation mode",
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.error,
                        modifier = Modifier.padding(vertical = 4.dp)
                    )
                }
                
                // Wake Word Custom Text (only show if wake word enabled)
                if (wakeWordEnabled) {
                    Spacer(modifier = Modifier.height(16.dp))
                    
                    Text(
                        text = "Custom Wake Word",
                        style = MaterialTheme.typography.bodyLarge,
                        modifier = Modifier.padding(bottom = 8.dp)
                    )
                    
                    OutlinedTextField(
                        value = customWakeWord,
                        onValueChange = { newValue ->
                            customWakeWord = newValue.lowercase().trim()
                            // Save to preferences
                            prefs.edit().putString("wake_word", customWakeWord).apply()
                        },
                        modifier = Modifier.fillMaxWidth(),
                        label = { Text("Wake word") },
                        placeholder = { Text("alfred") },
                        singleLine = true
                    )
                    
                    Text(
                        text = "Also supports: \"hey $customWakeWord\", \"ok $customWakeWord\"",
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        modifier = Modifier.padding(top = 4.dp)
                    )
                }
                
                Spacer(modifier = Modifier.height(16.dp))
                Divider()
                Spacer(modifier = Modifier.height(16.dp))
                
                // Wake Word Tone Toggle
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 8.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Column(modifier = Modifier.weight(1f)) {
                        Text(
                            text = "Wake Word Tone",
                            style = MaterialTheme.typography.bodyLarge
                        )
                        Text(
                            text = "Play beep when wake word detected",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant
                        )
                    }
                    var wakeWordTone by remember { 
                        mutableStateOf(prefs.getBoolean("wake_word_tone", false)) 
                    }
                    Switch(
                        checked = wakeWordTone,
                        onCheckedChange = { enabled ->
                            wakeWordTone = enabled
                            prefs.edit().putBoolean("wake_word_tone", enabled).apply()
                        }
                    )
                }
                
                Spacer(modifier = Modifier.height(16.dp))
                Divider()
                Spacer(modifier = Modifier.height(16.dp))
                
                // Gateway URL
                Text(
                    text = "Gateway URL",
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
                
                // Parse existing URL to hostname and protocol
                val existingUrl = prefs.getString("gateway_url", BuildConfig.GATEWAY_URL) ?: BuildConfig.GATEWAY_URL
                val initialHostname = existingUrl
                    .removePrefix("ws://")
                    .removePrefix("wss://")
                val initialInsecure = existingUrl.startsWith("ws://")
                
                var hostname by remember { mutableStateOf(initialHostname) }
                var useInsecure by remember { mutableStateOf(initialInsecure) }
                
                // Compute full URL
                val fullUrl = remember(hostname, useInsecure) {
                    if (hostname.isBlank()) {
                        ""
                    } else {
                        val protocol = if (useInsecure) "ws://" else "wss://"
                        "$protocol${hostname.trim()}"
                    }
                }
                
                OutlinedTextField(
                    value = hostname,
                    onValueChange = { newValue ->
                        hostname = newValue.trim()
                        // Save full URL to preferences
                        if (fullUrl.isNotEmpty()) {
                            prefs.edit().putString("gateway_url", fullUrl).apply()
                            Log.d("Settings", "Gateway URL changed to: $fullUrl (restart connection to apply)")
                        }
                    },
                    modifier = Modifier.fillMaxWidth(),
                    label = { Text("Hostname") },
                    placeholder = { Text("alfred.yourdomain.com") },
                    singleLine = true
                )
                
                androidx.compose.foundation.layout.Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 8.dp),
                    verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
                ) {
                    androidx.compose.material3.Checkbox(
                        checked = useInsecure,
                        onCheckedChange = { 
                            useInsecure = it
                            if (fullUrl.isNotEmpty()) {
                                prefs.edit().putString("gateway_url", fullUrl).apply()
                                Log.d("Settings", "Gateway URL changed to: $fullUrl (restart connection to apply)")
                            }
                        }
                    )
                    Text(
                        text = "Use insecure connection (ws://)",
                        style = MaterialTheme.typography.bodySmall,
                        modifier = Modifier.padding(start = 8.dp)
                    )
                }
                
                if (fullUrl.isNotEmpty()) {
                    Text(
                        text = "Full URL: $fullUrl",
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.primary,
                        modifier = Modifier.padding(top = 4.dp)
                    )
                }
                
                Text(
                    text = "Restart app after changing. No protocol needed - wss:// is added automatically.",
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(top = 4.dp)
                )
                
                Spacer(modifier = Modifier.height(16.dp))
                Divider()
                Spacer(modifier = Modifier.height(16.dp))
                
                // Assistant Name
                Text(
                    text = "Assistant Name",
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
                
                var assistantName by remember { 
                    mutableStateOf(prefs.getString("assistant_name", "Alfred") ?: "Alfred") 
                }
                
                OutlinedTextField(
                    value = assistantName,
                    onValueChange = { newValue ->
                        assistantName = newValue.trim()
                        // Save to preferences
                        prefs.edit().putString("assistant_name", assistantName).apply()
                        
                        // Update server preferences
                        scope.launch {
                            try {
                                val serviceIntent = Intent(context, com.openclaw.alfred.service.AlfredConnectionService::class.java)
                                val serviceConnection = object : ServiceConnection {
                                    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                                        val service = (binder as? com.openclaw.alfred.service.AlfredConnectionService.LocalBinder)?.getService()
                                        service?.getGatewayClient()?.updatePreferences(mapOf("assistantName" to assistantName))
                                        context.unbindService(this)
                                    }
                                    override fun onServiceDisconnected(name: ComponentName?) {}
                                }
                                context.bindService(serviceIntent, serviceConnection, android.content.Context.BIND_AUTO_CREATE)
                            } catch (e: Exception) {
                                Log.e("Settings", "Failed to update server preferences", e)
                            }
                        }
                    },
                    modifier = Modifier.fillMaxWidth(),
                    label = { Text("Name") },
                    placeholder = { Text("Alfred") },
                    singleLine = true
                )
                
                Text(
                    text = "What should the assistant call itself?",
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(top = 4.dp)
                )
                
                Spacer(modifier = Modifier.height(16.dp))
                Divider()
                Spacer(modifier = Modifier.height(16.dp))
                
                // Alarm Settings
                Text(
                    text = "Alarm Settings",
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
                
                // Alarm Sound Toggle
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = "Sound",
                        style = MaterialTheme.typography.bodyMedium
                    )
                    var alarmSound by remember { 
                        mutableStateOf(prefs.getBoolean("alarm_sound_enabled", true)) 
                    }
                    Switch(
                        checked = alarmSound,
                        onCheckedChange = { enabled ->
                            alarmSound = enabled
                            prefs.edit().putBoolean("alarm_sound_enabled", enabled).apply()
                        }
                    )
                }
                
                // Alarm Vibration Toggle
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = "Vibration",
                        style = MaterialTheme.typography.bodyMedium
                    )
                    var alarmVibrate by remember { 
                        mutableStateOf(prefs.getBoolean("alarm_vibrate_enabled", true)) 
                    }
                    Switch(
                        checked = alarmVibrate,
                        onCheckedChange = { enabled ->
                            alarmVibrate = enabled
                            prefs.edit().putBoolean("alarm_vibrate_enabled", enabled).apply()
                        }
                    )
                }
                
                Spacer(modifier = Modifier.height(8.dp))
                
                // Alarm Sound Picker Button
                Button(
                    onClick = {
                        val intent = android.content.Intent(android.media.RingtoneManager.ACTION_RINGTONE_PICKER).apply {
                            putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_TYPE, android.media.RingtoneManager.TYPE_ALARM)
                            putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_TITLE, "Select Alarm Sound")
                            putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
                            putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
                            alarmSoundUri?.let { uri ->
                                putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, android.net.Uri.parse(uri))
                            }
                        }
                        alarmSoundLauncher.launch(intent)
                    },
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Icon(
                        imageVector = androidx.compose.material.icons.Icons.Default.VolumeUp,
                        contentDescription = null,
                        modifier = Modifier.size(20.dp)
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text("Sound: $alarmSoundName")
                }
                
                // Reset to default button if custom sound is set
                if (alarmSoundUri != null) {
                    TextButton(
                        onClick = {
                            alarmSoundUri = null
                            alarmSoundName = "Default Alarm"
                            prefs.edit().remove("alarm_sound_uri").apply()
                            Toast.makeText(context, "Reset to default alarm sound", Toast.LENGTH_SHORT).show()
                        },
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Text("Reset to Default")
                    }
                }
                
                Spacer(modifier = Modifier.height(16.dp))
                Divider()
                Spacer(modifier = Modifier.height(16.dp))
                
                // Voice Selection
                Text(
                    text = "Voice Selection",
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
                
                if (isLoadingVoices) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.Center,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        CircularProgressIndicator(modifier = Modifier.size(24.dp))
                        Spacer(modifier = Modifier.width(8.dp))
                        Text("Loading voices...")
                    }
                } else if (voices.isEmpty()) {
                    Text(
                        text = "No voices available. Check SAG installation.",
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.error
                    )
                } else {
                    // Voice dropdown
                    Box(modifier = Modifier.fillMaxWidth()) {
                        OutlinedButton(
                            onClick = { showVoiceDropdown = true },
                            modifier = Modifier.fillMaxWidth()
                        ) {
                            Text(
                                text = com.openclaw.alfred.voice.VoiceHelper.getVoiceName(voices, selectedVoiceId),
                                modifier = Modifier.weight(1f)
                            )
                            Icon(
                                imageVector = if (showVoiceDropdown) 
                                    androidx.compose.material.icons.Icons.Default.ArrowDropUp 
                                else 
                                    androidx.compose.material.icons.Icons.Default.ArrowDropDown,
                                contentDescription = null
                            )
                        }
                        
                        DropdownMenu(
                            expanded = showVoiceDropdown,
                            onDismissRequest = { showVoiceDropdown = false },
                            modifier = Modifier.fillMaxWidth(0.9f)
                        ) {
                            voices.forEach { voice ->
                                DropdownMenuItem(
                                    text = {
                                        Column {
                                            Text(
                                                text = voice.name,
                                                style = MaterialTheme.typography.bodyMedium
                                            )
                                            Text(
                                                text = voice.category,
                                                style = MaterialTheme.typography.bodySmall,
                                                color = MaterialTheme.colorScheme.onSurfaceVariant
                                            )
                                        }
                                    },
                                    onClick = {
                                        selectedVoiceId = voice.id
                                        showVoiceDropdown = false
                                        // Save to preferences
                                        prefs.edit().putString("tts_voice_id", selectedVoiceId).apply()
                                        
                                        // Update server preferences
                                        scope.launch {
                                            try {
                                                val serviceIntent = Intent(context, com.openclaw.alfred.service.AlfredConnectionService::class.java)
                                                val serviceConnection = object : ServiceConnection {
                                                    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                                                        val service = (binder as? com.openclaw.alfred.service.AlfredConnectionService.LocalBinder)?.getService()
                                                        service?.getGatewayClient()?.updatePreferences(mapOf("voiceId" to selectedVoiceId))
                                                        context.unbindService(this)
                                                    }
                                                    override fun onServiceDisconnected(name: ComponentName?) {}
                                                }
                                                context.bindService(serviceIntent, serviceConnection, android.content.Context.BIND_AUTO_CREATE)
                                            } catch (e: Exception) {
                                                Log.e("Settings", "Failed to update server preferences", e)
                                            }
                                        }
                                    }
                                )
                            }
                        }
                    }
                    
                    Spacer(modifier = Modifier.height(8.dp))
                    
                    // Test Voice Button
                    Button(
                        onClick = {
                            isTesting = true
                            val ttsManager = com.openclaw.alfred.voice.TTSManager(context)
                            ttsManager.speak(
                                text = "Hello! This is a test of the voice you selected.",
                                onComplete = { 
                                    isTesting = false
                                    ttsManager.destroy()
                                },
                                onError = { 
                                    isTesting = false
                                    ttsManager.destroy()
                                    Toast.makeText(context, "Voice test failed", Toast.LENGTH_SHORT).show()
                                }
                            )
                        },
                        modifier = Modifier.fillMaxWidth(),
                        enabled = !isTesting && voices.isNotEmpty()
                    ) {
                        if (isTesting) {
                            CircularProgressIndicator(
                                modifier = Modifier.size(16.dp),
                                color = MaterialTheme.colorScheme.onPrimary
                            )
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                        Text(if (isTesting) "Testing..." else "Test Voice")
                    }
                }
            }
        },
        confirmButton = {
            TextButton(onClick = onDismiss) {
                Text("Save")
            }
        }
    )
}
