package coredevices.ring.storage import co.touchlab.kermit.Logger import coredevices.util.CoreConfigFlow import io.ktor.client.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put import org.koin.core.component.KoinComponent import org.koin.core.component.get @Serializable data class TranscriptionLogEntry( val recording_filename: String, val speech_recognizer_transcription: String, val transcription_time_ms: Long, val created_at: String? = null // Optional, will be auto-generated by Supabase ) class SupabaseTranscriptionLogger( private val httpClient: HttpClient, private val supabaseUrl: String, private val supabaseKey: String ) : KoinComponent { companion object { private val logger = Logger.withTag(SupabaseTranscriptionLogger::class.simpleName!!) private val json = Json { ignoreUnknownKeys = true encodeDefaults = true } } /** * Log transcription data to Supabase * @param recordingFilename The name/path of the recording file * @param transcription The transcribed text from speech recognizer * @param transcriptionTimeMs Time taken for transcription in milliseconds * @return true if logging was successful, false otherwise */ suspend fun logTranscription( recordingFilename: String, transcription: String, transcriptionTimeMs: Long ): Boolean = withContext(Dispatchers.Default) { try { logger.d { "Logging transcription data to Supabase" } logger.d { "Recording: $recordingFilename" } logger.d { "Transcription length: ${transcription.length} chars" } logger.d { "Time taken: ${transcriptionTimeMs}ms" } val entry = TranscriptionLogEntry( recording_filename = recordingFilename, speech_recognizer_transcription = transcription, transcription_time_ms = transcriptionTimeMs ) // Use the REST API URL format: https://your-project.supabase.co/rest/v1/table_name val apiUrl = supabaseUrl.replace("storage.supabase.co", "supabase.co") + "/rest/v1/transcriptions" logger.d { "API URL: $apiUrl" } val response = httpClient.post(apiUrl) { header("apikey", supabaseKey) header("Authorization", "Bearer $supabaseKey") header("Content-Type", "application/json") header("Prefer", "return=minimal") setBody(json.encodeToString(TranscriptionLogEntry.serializer(), entry)) } logger.d { "Transcription log response status: ${response.status}" } if (response.status.isSuccess()) { logger.i { "Transcription logged successfully to Supabase" } return@withContext true } else { val errorBody = response.bodyAsText() logger.e { "Failed to log transcription with status ${response.status}: $errorBody" } return@withContext false } } catch (e: Exception) { logger.e(e) { "Error logging transcription to Supabase: ${e.message}" } return@withContext false } } /** * Convert Any value to JsonElement for serialization */ private fun convertToJsonElement(value: Any): JsonElement { return when (value) { is String -> JsonPrimitive(value) is Number -> JsonPrimitive(value) is Boolean -> JsonPrimitive(value) is Float -> JsonPrimitive(value) is Double -> JsonPrimitive(value) is Int -> JsonPrimitive(value) is Long -> JsonPrimitive(value) else -> JsonPrimitive(value.toString()) } } /** * Log transcription data with additional metadata * @param recordingFilename The name/path of the recording file * @param transcription The transcribed text from speech recognizer * @param transcriptionTimeMs Time taken for transcription in milliseconds * @return true if logging was successful, false otherwise */ suspend fun log( recordingFilename: String, transcription: String, transcriptionTimeMs: Long ): Boolean = withContext(Dispatchers.Default) { try { logger.d { "Logging transcription data with metadata to Supabase" } // Build JSON object properly with type-safe conversion val jsonObject = buildJsonObject { put("recording_filename", recordingFilename) put("speech_recognizer_transcription", transcription) put("transcription_time_ms", transcriptionTimeMs) } // Use the REST API URL format: https://your-project.supabase.co/rest/v1/table_name val apiUrl = supabaseUrl.replace("storage.supabase.co", "supabase.co") + "/rest/v1/transcriptions" logger.d { "API URL: $apiUrl" } logger.d { "JSON data: ${if (get().value.obfuscateSensitiveLogs) "[redacted]" else jsonObject}" } val response = httpClient.post(apiUrl) { header("apikey", supabaseKey) header("Authorization", "Bearer $supabaseKey") header("Content-Type", "application/json") header("Prefer", "return=minimal") setBody(jsonObject.toString()) } logger.d { "Transcription log response status: ${response.status}" } if (response.status.isSuccess()) { logger.i { "Transcription with metadata logged successfully to Supabase" } return@withContext true } else { val errorBody = response.bodyAsText() logger.e { "Failed to log transcription with status ${response.status}: $errorBody" } return@withContext false } } catch (e: Exception) { logger.e(e) { "Error logging transcription with metadata to Supabase: ${e.message}" } return@withContext false } } }