package dev.privacyllc.period.core.database

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.withTransaction
import dev.privacyllc.period.core.database.dao.NotYetObservationDao
import dev.privacyllc.period.core.database.dao.PeriodRecordDao
import dev.privacyllc.period.core.database.dao.PredictionRecordDao
import dev.privacyllc.period.core.database.dao.SpottingRecordDao
import dev.privacyllc.period.core.database.entity.NotYetObservationEntity
import dev.privacyllc.period.core.database.entity.PeriodRecordEntity
import dev.privacyllc.period.core.database.entity.PredictionRecordEntity
import dev.privacyllc.period.core.database.entity.SpottingRecordEntity

/**
 * The cycle database.
 *
 * Lives in app-private storage and is excluded from platform backup — see
 * `AndroidManifest.xml`, `res/xml/data_extraction_rules.xml` and
 * docs/security/SECURITY.md. Android auto-backup is on by default, and a
 * default that ships a cycle history to a cloud account defeats the entire
 * local-first argument.
 *
 * **`exportSchema = true` and the schemas are committed.** A migration test can
 * only compare against a real previous schema if one was written down.
 */
@Database(
    entities = [
        PeriodRecordEntity::class,
        SpottingRecordEntity::class,
        PredictionRecordEntity::class,
        NotYetObservationEntity::class,
    ],
    version = PeriodDatabase.VERSION,
    exportSchema = true,
)
@TypeConverters(Converters::class)
abstract class PeriodDatabase : RoomDatabase() {

    abstract fun periodRecordDao(): PeriodRecordDao
    abstract fun spottingRecordDao(): SpottingRecordDao
    abstract fun predictionRecordDao(): PredictionRecordDao
    abstract fun notYetObservationDao(): NotYetObservationDao

    /**
     * Delete My Data, and the only bulk delete in this module.
     *
     * One transaction: a partial wipe that leaves prediction snapshots behind
     * would leave the user's cycle reconstructible from the very table they
     * asked to be rid of. PRODUCT_PLAN.md §45 — clear and irreversible after
     * confirmation.
     */
    suspend fun deleteEverything() = withTransaction {
        clearAllTables()
    }

    /**
     * Run [block] in one transaction.
     *
     * Exposed here rather than letting callers reach for `withTransaction`
     * directly, so `room-ktx` stays an `implementation` dependency of this
     * module and never reaches the compile classpath of anything above it.
     * Confirming a period is four writes that are meaningless apart — a partial
     * result is a corrupt history rather than a failed action.
     */
    suspend fun <R> inTransaction(block: suspend () -> R): R = withTransaction(block)

    companion object {
        const val VERSION = 1
        const val NAME = "period.db"
    }
}
