package com.example.nfcbluetoothtransfer

import android.Manifest
import android.app.PendingIntent
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.Ndef
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import com.example.nfcbluetoothtransfer.ui.theme.NFCBluetoothTransferTheme
import java.util.UUID

// ─────────────────────────────────────────────
// Custom font — place hitchcut_regular.ttf in
// app/src/main/res/font/
// ─────────────────────────────────────────────
val HitchcutFamily = FontFamily(
    Font(R.font.hitchcut_regular)
)

// ─────────────────────────────────────────────
// App state machine
// ─────────────────────────────────────────────
enum class AppState {
    WAITING_FIRST_TAP,
    PICKING_FILES,
    WAITING_SECOND_TAP,
    SENDING
}

class MainActivity : ComponentActivity() {

    private var nfcAdapter: NfcAdapter? = null
    private var bluetoothAdapter: BluetoothAdapter? = null
    private var detectedMac by mutableStateOf("")
    private var appState by mutableStateOf(AppState.WAITING_FIRST_TAP)
    private var selectedFiles by mutableStateOf<List<Uri>>(emptyList())
    private var statusMessage by mutableStateOf("")

    companion object {
        // Bluetooth OPP / OBEX Object Push Profile UUID
        val BT_UUID: UUID = UUID.fromString("00001105-0000-1000-8000-00805F9B34FB")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        nfcAdapter = NfcAdapter.getDefaultAdapter(this)
        val bluetoothManager = getSystemService(BluetoothManager::class.java)
        bluetoothAdapter = bluetoothManager.adapter

        // Request runtime Bluetooth permissions (Android 12+)
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.BLUETOOTH_CONNECT,
                Manifest.permission.BLUETOOTH_SCAN
            ),
            1
        )

        // Handle NFC if the app was cold-launched by a tag tap
        intent?.let { handleNfcIntent(it) }

        setContent {
            NFCBluetoothTransferTheme {
                MainScreen(
                    state = appState,
                    status = statusMessage,
                    selectedFiles = selectedFiles,
                    onFilesPicked = { uris ->
                        selectedFiles = uris
                        appState = AppState.WAITING_SECOND_TAP
                        statusMessage = ""
                    },
                    onReset = { resetApp() }
                )
            }
        }
    }

    private fun resetApp() {
        appState = AppState.WAITING_FIRST_TAP
        selectedFiles = emptyList()
        detectedMac = ""
        statusMessage = ""
    }

    override fun onResume() {
        super.onResume()
        val intent = Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_MUTABLE
        )
        nfcAdapter?.enableForegroundDispatch(this, pendingIntent, null, null)
    }

    override fun onPause() {
        super.onPause()
        nfcAdapter?.disableForegroundDispatch(this)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleNfcIntent(intent)
    }

    // ─────────────────────────────────────────
    // NFC handling
    // ─────────────────────────────────────────
    private fun handleNfcIntent(intent: Intent) {
        if (intent.action == NfcAdapter.ACTION_NDEF_DISCOVERED ||
            intent.action == NfcAdapter.ACTION_TAG_DISCOVERED
        ) {
            val tag: Tag? = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
            tag?.let { handleNfcTap(it) }
        }
    }

    private fun handleNfcTap(tag: Tag) {
        when (appState) {
            AppState.WAITING_FIRST_TAP -> readNfcTag(tag)
            AppState.WAITING_SECOND_TAP -> {
                if (selectedFiles.isEmpty()) {
                    statusMessage = "⚠️ No files selected!"
                    return
                }
                appState = AppState.SENDING
                sendFiles()
            }
            else -> {}
        }
    }

    /**
     * Reads the NTAG213 tag.
     * Expected tag content: "XX:XX:XX:XX:XX:XX"  (Bluetooth MAC address)
     * Optional extended format: "XX:XX:XX:XX:XX:XX|192.168.x.x" (MAC | laptop IP)
     */
    private fun readNfcTag(tag: Tag) {
        val ndef = Ndef.get(tag)
        ndef?.let {
            try {
                it.connect()
                val message = it.ndefMessage
                val record = message?.records?.firstOrNull()
                record?.let { r ->
                    val payload = String(r.payload).trimStart('\u0002', 'e', 'n').trim()
                    detectedMac = payload.split("|")[0].trim()
                    appState = AppState.PICKING_FILES
                    statusMessage = ""
                }
                it.close()
            } catch (e: Exception) {
                statusMessage = "❌ Could not read tag: ${e.message}"
            }
        }
    }

    // ─────────────────────────────────────────
    // Bluetooth OBEX file transfer
    // ─────────────────────────────────────────
    private fun sendFiles() {
        if (ActivityCompat.checkSelfPermission(
                this, Manifest.permission.BLUETOOTH_CONNECT
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            statusMessage = "❌ Bluetooth permission not granted!"
            appState = AppState.WAITING_SECOND_TAP
            return
        }

        Thread {
            try {
                val device = bluetoothAdapter?.getRemoteDevice(detectedMac)
                val socket = device!!.createInsecureRfcommSocketToServiceRecord(BT_UUID)
                socket.connect()

                val input = socket.inputStream
                val output = socket.outputStream

                // OBEX CONNECT
                output.write(byteArrayOf(0x80.toByte(), 0x00, 0x07, 0x10, 0x00, 0x20, 0x00))
                output.flush()
                val connResp = ByteArray(100)
                val connLen = input.read(connResp)
                if (connLen < 1 || connResp[0] != 0xA0.toByte()) {
                    runOnUiThread {
                        statusMessage = "❌ OBEX connect failed"
                        appState = AppState.WAITING_SECOND_TAP
                    }
                    socket.close()
                    return@Thread
                }

                // Send each file
                selectedFiles.forEachIndexed { index, uri ->
                    runOnUiThread {
                        statusMessage = "Sending ${index + 1}/${selectedFiles.size}..."
                    }

                    val fileBytes = contentResolver.openInputStream(uri)!!.readBytes()
                    val mimeType = contentResolver.getType(uri) ?: "*/*"
                    val ext = when {
                        mimeType.contains("jpeg") || mimeType.contains("jpg") -> "jpg"
                        mimeType.contains("png") -> "png"
                        mimeType.contains("pdf") -> "pdf"
                        mimeType.contains("mp4") -> "mp4"
                        mimeType.contains("audio") -> "mp3"
                        mimeType.contains("text") -> "txt"
                        else -> "bin"
                    }
                    val fileName = "file_${index + 1}.$ext"
                    val nameBytes = (fileName + "\u0000").toByteArray(Charsets.UTF_16BE)
                    val total = fileBytes.size
                    val chunkSize = 4096
                    var offset = 0
                    var firstPacket = true

                    while (offset < total) {
                        val isLast = (offset + chunkSize >= total)
                        val chunk = fileBytes.copyOfRange(offset, minOf(offset + chunkSize, total))
                        offset += chunk.size

                        val buf = java.io.ByteArrayOutputStream()
                        if (firstPacket) {
                            // Name header
                            buf.write(0x01)
                            val nl = nameBytes.size + 3
                            buf.write((nl shr 8) and 0xFF); buf.write(nl and 0xFF)
                            buf.write(nameBytes)
                            // Type header
                            val typeBytes = (mimeType + "\u0000").toByteArray(Charsets.US_ASCII)
                            buf.write(0x42)
                            val tl = typeBytes.size + 3
                            buf.write((tl shr 8) and 0xFF); buf.write(tl and 0xFF)
                            buf.write(typeBytes)
                            // Length header
                            buf.write(0xC3.toByte().toInt())
                            buf.write((total shr 24) and 0xFF)
                            buf.write((total shr 16) and 0xFF)
                            buf.write((total shr 8) and 0xFF)
                            buf.write(total and 0xFF)
                            firstPacket = false
                        }

                        // Body / End-of-Body
                        buf.write(if (isLast) 0x49 else 0x48)
                        val bl = chunk.size + 3
                        buf.write((bl shr 8) and 0xFF); buf.write(bl and 0xFF)
                        buf.write(chunk)

                        val headers = buf.toByteArray()
                        val pktLen = headers.size + 3
                        val pkt = java.io.ByteArrayOutputStream()
                        pkt.write(if (isLast) 0x82 else 0x02)
                        pkt.write((pktLen shr 8) and 0xFF)
                        pkt.write(pktLen and 0xFF)
                        pkt.write(headers)
                        output.write(pkt.toByteArray())
                        output.flush()

                        val resp = ByteArray(100)
                        input.read(resp)
                    }
                }

                // OBEX DISCONNECT
                output.write(byteArrayOf(0x81.toByte(), 0x00, 0x03))
                output.flush()
                Thread.sleep(300)
                socket.close()

                runOnUiThread {
                    statusMessage = "✅ Done!"
                    appState = AppState.WAITING_FIRST_TAP
                    selectedFiles = emptyList()
                    Toast.makeText(this, "Files sent!", Toast.LENGTH_SHORT).show()
                }

            } catch (e: Exception) {
                runOnUiThread {
                    statusMessage = "❌ ${e.message}"
                    appState = AppState.WAITING_SECOND_TAP
                }
            }
        }.start()
    }
}

// ─────────────────────────────────────────────────────────────
// UI
// ─────────────────────────────────────────────────────────────
@Composable
fun MainScreen(
    state: AppState,
    status: String,
    selectedFiles: List<Uri>,
    onFilesPicked: (List<Uri>) -> Unit,
    onReset: () -> Unit
) {
    val multiFileLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetMultipleContents()
    ) { uris: List<Uri> ->
        if (uris.isNotEmpty()) onFilesPicked(uris)
    }

    LaunchedEffect(state) {
        if (state == AppState.PICKING_FILES) {
            multiFileLauncher.launch("*/*")
        }
    }

    val pulseAnim = rememberInfiniteTransition(label = "pulse")
    val pulse by pulseAnim.animateFloat(
        initialValue = 0.85f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(900, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "pulse"
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFF0A0A0A))
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 24.dp, vertical = 48.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // ── Logo ──────────────────────────────────
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.Center
            ) {
                Text(
                    text = "OLII",
                    fontSize = 28.sp,
                    fontWeight = FontWeight.Normal,
                    fontFamily = HitchcutFamily,
                    letterSpacing = 3.sp,
                    color = Color.White
                )
                Text(
                    text = "-8882",
                    fontSize = 28.sp,
                    fontWeight = FontWeight.Black,
                    fontFamily = HitchcutFamily,
                    color = Color(0xFF2864FF),
                    letterSpacing = 2.sp
                )
            }
            Text(
                text = "NFC TRANSFER",
                fontSize = 11.sp,
                fontWeight = FontWeight.Medium,
                color = Color(0xFF555555),
                letterSpacing = 6.sp
            )

            Spacer(modifier = Modifier.height(52.dp))

            // ── Pulsing NFC circle ────────────────────
            Box(contentAlignment = Alignment.Center) {
                Box(
                    modifier = Modifier
                        .size((120 * pulse).dp)
                        .background(
                            color = Color(0xFF2864FF).copy(
                                alpha = if (state == AppState.WAITING_FIRST_TAP ||
                                    state == AppState.WAITING_SECOND_TAP) 0.07f else 0f
                            ),
                            shape = CircleShape
                        )
                )
                Box(
                    modifier = Modifier
                        .size((88 * pulse).dp)
                        .background(
                            color = Color(0xFF2864FF).copy(
                                alpha = if (state == AppState.WAITING_FIRST_TAP ||
                                    state == AppState.WAITING_SECOND_TAP) 0.12f else 0f
                            ),
                            shape = CircleShape
                        )
                )
                Box(
                    modifier = Modifier
                        .size(64.dp)
                        .background(
                            color = when (state) {
                                AppState.WAITING_FIRST_TAP,
                                AppState.WAITING_SECOND_TAP -> Color(0xFF2864FF)
                                AppState.PICKING_FILES -> Color(0xFF1C1C1C)
                                AppState.SENDING -> Color(0xFF00C853)
                            },
                            shape = CircleShape
                        ),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = when (state) {
                            AppState.WAITING_FIRST_TAP,
                            AppState.WAITING_SECOND_TAP -> "NFC"
                            AppState.PICKING_FILES -> "📁"
                            AppState.SENDING -> "✓"
                        },
                        fontSize = when (state) {
                            AppState.PICKING_FILES -> 22.sp
                            else -> 13.sp
                        },
                        fontWeight = FontWeight.Black,
                        fontFamily = HitchcutFamily,
                        color = Color.White,
                        letterSpacing = 2.sp
                    )
                }
            }

            Spacer(modifier = Modifier.height(28.dp))

            // ── State label ───────────────────────────
            Text(
                text = when (state) {
                    AppState.WAITING_FIRST_TAP -> "TAP YOUR TAG"
                    AppState.PICKING_FILES -> "PICK YOUR FILES"
                    AppState.WAITING_SECOND_TAP -> "TAP AGAIN TO SEND"
                    AppState.SENDING -> "SENDING..."
                },
                fontSize = 20.sp,
                fontWeight = FontWeight.Black,
                fontFamily = HitchcutFamily,
                color = Color.White,
                letterSpacing = 3.sp
            )

            Text(
                text = when (state) {
                    AppState.WAITING_FIRST_TAP -> "Make sure NFC is activated on your phone"
                    AppState.PICKING_FILES -> "Select one or multiple files"
                    AppState.WAITING_SECOND_TAP -> "${selectedFiles.size} file(s) ready to transfer"
                    AppState.SENDING -> "Transferring via Bluetooth..."
                },
                fontSize = 12.sp,
                color = Color(0xFF666666),
                modifier = Modifier.padding(top = 6.dp),
                textAlign = TextAlign.Center
            )

            Spacer(modifier = Modifier.height(44.dp))

            // ── Step indicators ───────────────────────
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceEvenly,
                verticalAlignment = Alignment.CenterVertically
            ) {
                OliiStep("01", "TAP",
                    done = state != AppState.WAITING_FIRST_TAP,
                    active = state == AppState.WAITING_FIRST_TAP)
                StepDivider(active = state != AppState.WAITING_FIRST_TAP)
                OliiStep("02", "PICK",
                    done = state == AppState.WAITING_SECOND_TAP || state == AppState.SENDING,
                    active = state == AppState.PICKING_FILES)
                StepDivider(active = state == AppState.WAITING_SECOND_TAP || state == AppState.SENDING)
                OliiStep("03", "TAP",
                    done = state == AppState.SENDING,
                    active = state == AppState.WAITING_SECOND_TAP)
            }

            Spacer(modifier = Modifier.height(28.dp))

            // ── Status / error card ───────────────────
            if (status.isNotEmpty()) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(
                            if (status.contains("❌") || status.contains("⚠️"))
                                Color(0xFF180808) else Color(0xFF0A1A0A),
                            RoundedCornerShape(10.dp)
                        )
                        .border(
                            1.dp,
                            if (status.contains("❌") || status.contains("⚠️"))
                                Color(0xFF3A0000) else Color(0xFF003A00),
                            RoundedCornerShape(10.dp)
                        )
                        .padding(14.dp)
                ) {
                    Text(
                        text = status,
                        color = if (status.contains("❌") || status.contains("⚠️"))
                            Color(0xFFFF4444) else Color(0xFF44FF88),
                        fontSize = 12.sp,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.fillMaxWidth()
                    )
                }
            }

            Spacer(modifier = Modifier.weight(1f))

            // ── Reset button ──────────────────────────
            OutlinedButton(
                onClick = onReset,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(48.dp),
                shape = RoundedCornerShape(6.dp),
                border = BorderStroke(1.dp, Color(0xFF2A2A2A)),
                colors = ButtonDefaults.outlinedButtonColors(
                    contentColor = Color(0xFF555555)
                )
            ) {
                Text(
                    "RESET",
                    fontSize = 11.sp,
                    fontWeight = FontWeight.Bold,
                    fontFamily = HitchcutFamily,
                    letterSpacing = 4.sp
                )
            }

            Spacer(modifier = Modifier.height(12.dp))

            Text(
                "Try Everything ! — Olii-8882",
                fontSize = 9.sp,
                color = Color(0xFF2A2A2A),
                fontFamily = HitchcutFamily,
                letterSpacing = 2.sp
            )
        }
    }
}

@Composable
fun OliiStep(number: String, label: String, done: Boolean, active: Boolean) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Box(
            modifier = Modifier
                .size(38.dp)
                .background(
                    color = when {
                        done -> Color(0xFF2864FF)
                        active -> Color(0xFF0D1A33)
                        else -> Color(0xFF111111)
                    },
                    shape = CircleShape
                )
                .border(
                    1.dp,
                    when {
                        done || active -> Color(0xFF2864FF)
                        else -> Color(0xFF1E1E1E)
                    },
                    CircleShape
                ),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = if (done) "✓" else number,
                fontSize = 11.sp,
                fontWeight = FontWeight.Bold,
                fontFamily = HitchcutFamily,
                color = when {
                    done -> Color.White
                    active -> Color(0xFF2864FF)
                    else -> Color(0xFF333333)
                }
            )
        }
        Spacer(modifier = Modifier.height(5.dp))
        Text(
            text = label,
            fontSize = 9.sp,
            fontWeight = FontWeight.Bold,
            fontFamily = HitchcutFamily,
            color = when {
                done || active -> Color(0xFF2864FF)
                else -> Color(0xFF333333)
            },
            letterSpacing = 2.sp
        )
    }
}

@Composable
fun StepDivider(active: Boolean) {
    Box(
        modifier = Modifier
            .padding(bottom = 18.dp)
            .width(36.dp)
            .height(1.dp)
            .background(if (active) Color(0xFF2864FF) else Color(0xFF1E1E1E))
    )
}
