package com.korancrew.brizzi.hce

/**
 * AID yang dikenali saat ini.
 */
enum class SelectedAid {
    NONE,
    OTHER,
    AID1,
    AID3,
}

/** Tipe transaksi yang dapat berjalan di sesi. */
enum class TransactionKind {
    DEBIT,
    CREDIT,
}

/** Fase transaksi dalam satu sesi transaksi AID3. */
enum class TransactionPhase {
    NONE,
    DEBIT_PENDING,
    CREDIT_PENDING,
    LOG_PENDING,
    LAST_TXN_PENDING,
}

/** Fase autentikasi kartu/NFC session. */
enum class SessionPhase {
    NONE,
    AID1_SELECTED,
    AID3_SELECTED,
    AID3_AUTHENTICATED,
}

data class PendingTransaction(
    val kind: TransactionKind,
    val amount: Int,
    val balanceBefore: Int,
    val balanceAfter: Int,
)

class BrizziSession {
    var selectedAid: SelectedAid = SelectedAid.NONE
    var authenticated: Boolean = false
    var continuationFrames: MutableList<String> = mutableListOf()
    var phase: SessionPhase = SessionPhase.NONE
    var transactionPhase: TransactionPhase = TransactionPhase.NONE
    var pendingTransaction: PendingTransaction? = null
    var pendingLogHex: String? = null
    var pendingLastTransactionDateHex: String? = null
    var pendingAkumDebetHex: String? = null
    var readableFallbackStep: Int = 0
    var lastTouchedAtEpochMs: Long = System.currentTimeMillis()
    private var replayWindowCommandHex: String? = null
    private var replayWindowAtEpochMs: Long = 0L

    /**
     * Reset total state (dipanggil saat deactivation / start sesi baru).
     */
    fun reset() {
        selectedAid = SelectedAid.NONE
        authenticated = false
        phase = SessionPhase.NONE
        transactionPhase = TransactionPhase.NONE
        continuationFrames.clear()
        pendingTransaction = null
        pendingLogHex = null
        pendingLastTransactionDateHex = null
        pendingAkumDebetHex = null
        readableFallbackStep = 0
        lastTouchedAtEpochMs = System.currentTimeMillis()
        replayWindowCommandHex = null
        replayWindowAtEpochMs = 0L
    }

    fun clearPendingTransactionState() {
        pendingTransaction = null
        pendingLogHex = null
        pendingLastTransactionDateHex = null
        pendingAkumDebetHex = null
        transactionPhase = TransactionPhase.NONE
    }

    /** Bolehkan command read-only (mis. saldo/log) setelah AID dipilih. */
    fun isReadyForReadOnlyCommand(): Boolean =
        selectedAid != SelectedAid.NONE

    /** Pilih AID tidak dikenal dari reader dan reset state transaksi sebelumnya. */
    fun selectUnknownAid() {
        selectedAid = SelectedAid.OTHER
        authenticated = false
        phase = SessionPhase.NONE
        transactionPhase = TransactionPhase.NONE
        readableFallbackStep = 0
        clearPendingTransactionState()
        continuationFrames.clear()
        replayWindowCommandHex = null
        replayWindowAtEpochMs = 0L
    }

    /** Pilih AID1 dan reset state transaksi sebelumnya. */
    fun selectAid1() {
        selectedAid = SelectedAid.AID1
        authenticated = false
        phase = SessionPhase.AID1_SELECTED
        transactionPhase = TransactionPhase.NONE
        readableFallbackStep = 0
        clearPendingTransactionState()
        continuationFrames.clear()
        replayWindowCommandHex = null
        replayWindowAtEpochMs = 0L
    }

    /** Pilih AID3 dan reset state transaksi sebelumnya. */
    fun selectAid3() {
        selectedAid = SelectedAid.AID3
        authenticated = false
        phase = SessionPhase.AID3_SELECTED
        transactionPhase = TransactionPhase.NONE
        readableFallbackStep = 0
        clearPendingTransactionState()
        continuationFrames.clear()
        replayWindowCommandHex = null
        replayWindowAtEpochMs = 0L
    }

    /** Tandai sesi sudah melewati autentikasi key exchange (khusus AID3). */
    fun authenticate() {
        authenticated = true
        if (selectedAid == SelectedAid.AID3) {
            phase = SessionPhase.AID3_AUTHENTICATED
        }
    }

    fun startDebitTransaction() {
        transactionPhase = TransactionPhase.DEBIT_PENDING
    }

    fun startCreditTransaction() {
        transactionPhase = TransactionPhase.CREDIT_PENDING
    }

    fun startLogUpdate() {
        transactionPhase = TransactionPhase.LOG_PENDING
    }

    fun startLastTxnUpdate() {
        transactionPhase = TransactionPhase.LAST_TXN_PENDING
    }

    fun isReadyForCommandWithAuth(): Boolean =
        phase == SessionPhase.AID3_AUTHENTICATED && selectedAid == SelectedAid.AID3

    /** Baru menerima command transaksi bila belum ada pending transaction lain. */
    fun isReadyForNewTransaction(): Boolean =
        isReadyForCommandWithAuth() && transactionPhase == TransactionPhase.NONE

    /** true jika sedang ada debit/credit/log/last transaction yang belum di-commit. */
    fun hasPendingTransactionState(): Boolean = transactionPhase != TransactionPhase.NONE

    /** true bila sesi idle lebih lama dari timeout yang diset. */
    fun isTimedOut(nowEpochMs: Long, timeoutMs: Long): Boolean =
        (nowEpochMs - lastTouchedAtEpochMs) > timeoutMs

    /** Update heartbeat sesi untuk mencegah timeout. */
    fun touch(nowEpochMs: Long = System.currentTimeMillis()) {
        lastTouchedAtEpochMs = nowEpochMs
    }

    /**
     * Deteksi replay untuk command sensitif.
     * Jika command sama datang lagi di dalam window, return true.
     */
    fun isReplay(windowMillis: Long, commandHex: String, nowEpochMs: Long): Boolean {
        val normalized = commandHex.uppercase()
        if (replayWindowCommandHex == normalized && (nowEpochMs - replayWindowAtEpochMs) <= windowMillis) {
            return true
        }
        replayWindowCommandHex = normalized
        replayWindowAtEpochMs = nowEpochMs
        return false
    }

    fun clearReplayWindow() {
        replayWindowCommandHex = null
        replayWindowAtEpochMs = 0L
    }
}
