package com.fnregistry.unibus

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import fn.compose.theme.FnSpacing
import fn.compose.theme.FnTheme
import fn.compose.ui.FnBadge
import fn.compose.ui.FnBadgeColor
import fn.compose.ui.FnButton
import fn.compose.ui.FnButtonVariant
import fn.compose.ui.FnCard
import fn.compose.ui.FnGroup
import fn.compose.ui.FnStack
import fn.compose.ui.FnSwitch
import fn.compose.ui.FnText
import fn.compose.ui.FnTextInput
import fn.compose.ui.FnTextSize
import fn.compose.ui.FnTitle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mobile.FrameListener
import mobile.Mobile
import mobile.Session
import java.io.File

/**
 * Single-activity chat client for the unibus message bus. The whole protocol —
 * NATS data plane, HTTP control plane and end-to-end crypto — lives inside the
 * gomobile-built unibus.aar (package `mobile`); this UI only orchestrates calls
 * to a [Session] and renders the frames it delivers.
 */
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // The long-term identity (Ed25519 + X25519 keys) is stored in the app's
        // private files directory, unreadable by other apps.
        val idPath = File(filesDir, "peer.id").absolutePath
        setContent {
            FnTheme {
                Surface(Modifier.fillMaxSize()) {
                    ChatApp(idPath)
                }
            }
        }
    }
}

@Composable
private fun ChatApp(idPath: String) {
    val scope = rememberCoroutineScope()
    // onFrame arrives on a NATS delivery thread; hop to the main thread before
    // touching Compose state.
    val mainHandler = remember { Handler(Looper.getMainLooper()) }

    // 10.0.2.2 is the host machine as seen from the Android emulator. On a real
    // phone, replace it with the PC/VPS LAN or Tailscale address.
    var host by remember { mutableStateOf("10.0.2.2") }
    var natsPort by remember { mutableStateOf("4250") }
    var ctrlPort by remember { mutableStateOf("8470") }
    var subject by remember { mutableStateOf("room.general") }
    var e2e by remember { mutableStateOf(false) }

    var session by remember { mutableStateOf<Session?>(null) }
    var roomId by remember { mutableStateOf<String?>(null) }
    var status by remember { mutableStateOf("Desconectado") }
    var draft by remember { mutableStateOf("") }
    val messages = remember { mutableStateListOf<String>() }

    val listener = remember {
        object : FrameListener {
            override fun onFrame(roomID: String, sender: String, msgID: String, text: String) {
                mainHandler.post { messages.add("${sender.take(6)} › $text") }
            }
        }
    }

    FnStack(
        modifier = Modifier.fillMaxSize().padding(FnSpacing.md),
        gap = FnSpacing.sm,
    ) {
        FnTitle("Unibus", order = 2)
        FnText(status, size = FnTextSize.Sm)

        when {
            // --- Step 1: connect to the bus ---
            session == null -> {
                FnTextInput(
                    value = host,
                    onValueChange = { host = it },
                    label = "Host",
                    placeholder = "10.0.2.2 / IP LAN / tailscale",
                    modifier = Modifier.fillMaxWidth(),
                )
                FnGroup(gap = FnSpacing.sm) {
                    FnTextInput(value = natsPort, onValueChange = { natsPort = it }, label = "NATS", modifier = Modifier.weight(1f))
                    FnTextInput(value = ctrlPort, onValueChange = { ctrlPort = it }, label = "Control", modifier = Modifier.weight(1f))
                }
                FnButton("Conectar", onClick = {
                    status = "Conectando…"
                    scope.launch {
                        try {
                            val s = withContext(Dispatchers.IO) {
                                Mobile.newSession(idPath, "nats://$host:$natsPort", "http://$host:$ctrlPort")
                            }
                            session = s
                            status = "Conectado · ${s.endpointID().take(10)}"
                        } catch (e: Exception) {
                            status = "Error al conectar: ${e.message}"
                        }
                    }
                }, modifier = Modifier.fillMaxWidth())
            }

            // --- Step 2: open a room ---
            roomId == null -> {
                FnTextInput(
                    value = subject,
                    onValueChange = { subject = it },
                    label = "Subject de la sala",
                    placeholder = "room.general",
                    modifier = Modifier.fillMaxWidth(),
                )
                FnSwitch(checked = e2e, onCheckedChange = { e2e = it }, label = "Cifrado E2E (modo matrix)")
                FnButton("Crear sala", onClick = {
                    status = "Creando sala…"
                    scope.launch {
                        try {
                            val mode = if (e2e) "matrix" else "nats"
                            val rid = withContext(Dispatchers.IO) {
                                val r = session!!.createRoom(subject, mode)
                                session!!.join(r)
                                session!!.subscribe(r, listener)
                                r
                            }
                            roomId = rid
                            status = "Sala activa"
                        } catch (e: Exception) {
                            status = "Error al crear sala: ${e.message}"
                        }
                    }
                }, modifier = Modifier.fillMaxWidth())
            }

            // --- Step 3: chat ---
            else -> {
                FnGroup(gap = FnSpacing.sm) {
                    FnBadge(if (e2e) "E2E" else "PLAIN", color = if (e2e) FnBadgeColor.Green else FnBadgeColor.Gray)
                    FnText(subject, size = FnTextSize.Xs)
                }
                FnCard(modifier = Modifier.fillMaxWidth().weight(1f)) {
                    FnStack(
                        modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
                        gap = FnSpacing.xs,
                    ) {
                        if (messages.isEmpty()) {
                            FnText("Sin mensajes todavía. Envía el primero.", size = FnTextSize.Sm)
                        } else {
                            messages.forEach { FnText(it, size = FnTextSize.Sm) }
                        }
                    }
                }
                FnGroup(gap = FnSpacing.sm) {
                    FnTextInput(
                        value = draft,
                        onValueChange = { draft = it },
                        label = "Mensaje",
                        modifier = Modifier.weight(1f),
                    )
                    FnButton("Enviar", onClick = {
                        val text = draft.trim()
                        if (text.isEmpty()) return@FnButton
                        draft = ""
                        scope.launch {
                            try {
                                withContext(Dispatchers.IO) { session!!.publish(roomId!!, text) }
                            } catch (e: Exception) {
                                status = "Error al enviar: ${e.message}"
                            }
                        }
                    }, variant = FnButtonVariant.Filled)
                }
            }
        }
    }
}
