package com.estateunified.panel import com.google.gson.Gson import com.google.gson.reflect.TypeToken import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path internal data class FavoriteEntry( val userId: String, val listingId: String, val createdAtIso: String = java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(java.time.LocalDateTime.now()), ) internal class FavoritesStore( private val gson: Gson, private val file: Path, ) { private val lock = Any() fun listForUser(userId: String): List = synchronized(lock) { load().filter { it.userId == userId } } fun has(userId: String, listingId: String): Boolean = synchronized(lock) { load().any { it.userId == userId && it.listingId == listingId } } fun toggle(userId: String, listingId: String): Boolean = synchronized(lock) { val list = load().toMutableList() val idx = list.indexOfFirst { it.userId == userId && it.listingId == listingId } val added: Boolean if (idx >= 0) { list.removeAt(idx) added = false } else { list.add(0, FavoriteEntry(userId, listingId)) added = true } persist(list) added } private fun load(): MutableList { if (!Files.isRegularFile(file)) return mutableListOf() val text = Files.readString(file, StandardCharsets.UTF_8) if (text.isBlank()) return mutableListOf() return runCatching { gson.fromJson>(text, listType) ?: mutableListOf() }.getOrElse { mutableListOf() } } private fun persist(list: List) { Files.createDirectories(file.toAbsolutePath().parent) Files.writeString(file, gson.toJson(list), StandardCharsets.UTF_8) } companion object { private val listType = object : TypeToken>() {}.type } }