package id.iiyh.inatrading

import android.util.Base64
import android.nfc.NdefRecord
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.Ndef
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import dagger.hilt.android.AndroidEntryPoint
import id.iiyh.inatrading.core.ui.theme.InaTradingTheme
import id.iiyh.inatrading.navigation.AppNavigation
import java.nio.charset.Charset
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    private var nfcAdapter: NfcAdapter? = null
    private var onRfidValueDetected: ((String) -> Unit)? = null
    private var onRfidError: ((String) -> Unit)? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        nfcAdapter = NfcAdapter.getDefaultAdapter(this)
        setContent {
            InaTradingTheme {
                AppNavigation()
            }
        }
    }

    fun startRfidScan(
        onValueDetected: (String) -> Unit,
        onError: (String) -> Unit,
    ): Boolean {
        val adapter = nfcAdapter ?: return false
        if (!adapter.isEnabled) {
            onError(getString(R.string.rfid_nfc_disabled))
            return true
        }

        onRfidValueDetected = onValueDetected
        onRfidError = onError
        adapter.enableReaderMode(
            this,
            { tag ->
                val matchedValue = extractInaTradingValue(tag)
                runOnUiThread {
                    if (matchedValue != null) {
                        val callback = onRfidValueDetected
                        stopRfidScan()
                        callback?.invoke(matchedValue)
                    } else {
                        onRfidError?.invoke(getString(R.string.rfid_invalid_tag))
                    }
                }
            },
            NfcAdapter.FLAG_READER_NFC_A or
                NfcAdapter.FLAG_READER_NFC_B or
                NfcAdapter.FLAG_READER_NFC_F or
                NfcAdapter.FLAG_READER_NFC_V or
                NfcAdapter.FLAG_READER_NFC_BARCODE,
            null,
        )
        return true
    }

    fun stopRfidScan() {
        nfcAdapter?.disableReaderMode(this)
        onRfidValueDetected = null
        onRfidError = null
    }

    override fun onPause() {
        super.onPause()
        stopRfidScan()
    }

    private fun extractInaTradingValue(tag: Tag): String? {
        val payloads = buildList<String> {
            val ndef = Ndef.get(tag)
            if (ndef != null) {
                runCatching {
                    ndef.connect()
                    ndef.cachedNdefMessage?.records
                        ?.mapNotNull(::recordToText)
                        ?.let(::addAll)
                    ndef.ndefMessage?.records
                        ?.mapNotNull(::recordToText)
                        ?.let(::addAll)
                }
                runCatching { if (ndef.isConnected) ndef.close() }
            }
        }

        val rawValue = payloads.firstOrNull { it.startsWith(RFID_PREFIX) } ?: return null
        val encryptedPart = rawValue.substringAfter(':', missingDelimiterValue = "").trim()
        if (encryptedPart.isBlank()) return null
        return decryptInaTradingValue(encryptedPart).takeIf { it.isNotBlank() }
    }

    private fun recordToText(record: NdefRecord): String? {
        return when {
            record.tnf == NdefRecord.TNF_WELL_KNOWN &&
                record.type.contentEquals(NdefRecord.RTD_TEXT) -> parseTextRecord(record.payload)
            else -> record.payload.toString(Charsets.UTF_8).trim().takeIf { it.isNotBlank() }
        }
    }

    private fun parseTextRecord(payload: ByteArray): String? {
        if (payload.isEmpty()) return null
        val status = payload[0].toInt()
        val isUtf16 = status and 0x80 != 0
        val languageCodeLength = status and 0x3F
        if (payload.size <= languageCodeLength + 1) return null
        val textBytes = payload.copyOfRange(languageCodeLength + 1, payload.size)
        val charset = if (isUtf16) Charset.forName("UTF-16") else Charsets.UTF_8
        return textBytes.toString(charset).trim()
    }

    private fun decryptInaTradingValue(cipherText: String): String {
        return runCatching {
            val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
            val keySpec = PBEKeySpec(
                AES_SECRET_KEY.toCharArray(),
                AES_SALT.toByteArray(Charsets.UTF_8),
                65536,
                256,
            )
            val secretKey = SecretKeySpec(
                keyFactory.generateSecret(keySpec).encoded,
                "AES",
            )
            val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
            val ivSpec = IvParameterSpec(ByteArray(16))
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
            val decodedBytes = Base64.decode(cipherText, Base64.NO_WRAP)
            val decryptedBytes = cipher.doFinal(decodedBytes)
            String(decryptedBytes, Charsets.UTF_8)
        }.getOrDefault("")
    }

    private companion object {
        const val RFID_PREFIX = "inaTrading:"
        const val AES_SECRET_KEY = "inaTrading"
        const val AES_SALT = "this is hard!!!!"
    }
}
