package no.svorka.mcms.ui

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
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.FontWeight
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import no.svorka.mcms.core.*

class OltListModel(private val connection: McmsConnection) {
    var phase by mutableStateOf<LoadPhase>(LoadPhase.Idle)
    var olts by mutableStateOf<List<OltStateDoc>>(emptyList())
    var search by mutableStateOf("")

    val filtered: List<OltStateDoc>
        get() {
            val q = search.trim()
            if (q.isEmpty()) return olts
            return olts.filter { it.id.contains(q, true) || (it.resolvedName?.contains(q, true) == true) }
        }

    private var loadGen = 0

    suspend fun load() {
        val gen = ++loadGen
        phase = LoadPhase.Loading
        try {
            val fetched = connection.olt.allStates()
            val names = runCatching { connection.olt.nameMap() }.getOrDefault(emptyMap())
            fetched.forEach { it.resolvedName = names[it.id] }
            val sorted = fetched.sortedBy { it.displayName.lowercase() }
            if (gen != loadGen) return
            olts = sorted
            phase = LoadPhase.Loaded
        } catch (e: CancellationException) {
            throw e
        } catch (e: Throwable) {
            if (gen == loadGen) phase = LoadPhase.Failed(e.uiMessage())
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OltListScreen(connection: McmsConnection, openOlt: (String, String) -> Unit) {
    val model = remember(connection) { OltListModel(connection) }
    val scope = rememberCoroutineScope()
    LaunchedEffect(connection) { if (model.phase is LoadPhase.Idle) model.load() }

    Scaffold(topBar = { TopAppBar(title = { Text("OLTs") }) }) { padding ->
        Column(Modifier.padding(padding).fillMaxSize()) {
            OutlinedTextField(
                value = model.search, onValueChange = { model.search = it },
                placeholder = { Text("OLT name or MAC") }, singleLine = true,
                modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 6.dp),
            )
            when (val p = model.phase) {
                is LoadPhase.Idle, is LoadPhase.Loading -> if (model.olts.isEmpty()) LoadingView("Loading OLTs…") else OltList(model.filtered, openOlt)
                is LoadPhase.Failed -> ErrorState(p.message) { scope.launch { model.load() } }
                is LoadPhase.Loaded -> OltList(model.filtered, openOlt)
            }
        }
    }
}

@Composable
private fun OltList(olts: List<OltStateDoc>, openOlt: (String, String) -> Unit) {
    LazyColumn(Modifier.fillMaxSize()) {
        items(olts) { olt ->
            ListRow(onClick = { openOlt(olt.id, olt.displayName) }) {
                Column(Modifier.weight(1f)) {
                    Text(olt.displayName, fontWeight = FontWeight.Medium)
                    Text("${olt.totalOnus} ONUs · ${olt.onlineOnus} online", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
                }
                val down = olt.totalOnus - olt.onlineOnus
                when {
                    olt.totalOnus == 0 -> StateBadge("no ONUs", Color(0xFF9E9E9E))
                    down > 0 -> StateBadge("$down down", Color(0xFFEF6C00))
                    else -> StateBadge("all up", Color(0xFF2E7D32))
                }
            }
            HorizontalDivider()
        }
    }
}

// ───────────────────────── Detail ─────────────────────────

class OltDetailModel(private val connection: McmsConnection, val oltId: String) {
    var phase by mutableStateOf<LoadPhase>(LoadPhase.Idle)
    var olt by mutableStateOf<OltStateDoc?>(null)
    var actionMessage by mutableStateOf<String?>(null)
    var busy by mutableStateOf(false)

    private var loadGen = 0

    suspend fun load() {
        val gen = ++loadGen
        phase = LoadPhase.Loading
        try {
            val v = connection.olt.state(oltId)
            if (gen != loadGen) return
            olt = v
            phase = LoadPhase.Loaded
        } catch (e: CancellationException) {
            throw e
        } catch (e: Throwable) {
            if (gen == loadGen) phase = LoadPhase.Failed(e.uiMessage())
        }
    }

    suspend fun reset() {
        if (busy) return
        busy = true; actionMessage = null
        try { connection.olt.reset(oltId); actionMessage = "Reset requested." }
        catch (e: CancellationException) { throw e }
        catch (e: Throwable) { actionMessage = e.uiMessage() }
        finally { busy = false }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OltDetailScreen(connection: McmsConnection, oltId: String, title: String, onBack: () -> Unit, openOnu: (String) -> Unit) {
    val model = remember(oltId) { OltDetailModel(connection, oltId) }
    val scope = rememberCoroutineScope()
    var showReset by remember { mutableStateOf(false) }
    var menu by remember { mutableStateOf(false) }
    LaunchedEffect(oltId) { model.load() }

    Scaffold(topBar = {
        TopAppBar(
            title = { Text(title) },
            navigationIcon = { BackButton(onBack) },
            actions = {
                IconButton(onClick = { menu = true }) { Icon(Icons.Filled.MoreVert, "Actions") }
                DropdownMenu(expanded = menu, onDismissRequest = { menu = false }) {
                    DropdownMenuItem(text = { Text("Reset OLT", color = MaterialTheme.colorScheme.error) },
                        enabled = !model.busy, onClick = { menu = false; showReset = true })
                }
            },
        )
    }) { padding ->
        Box(Modifier.padding(padding).fillMaxSize()) {
            when (val p = model.phase) {
                is LoadPhase.Idle, is LoadPhase.Loading -> if (model.olt == null) LoadingView() else OltContent(model.olt!!, openOnu)
                is LoadPhase.Failed -> ErrorState(p.message) { scope.launch { model.load() } }
                is LoadPhase.Loaded -> model.olt?.let { OltContent(it, openOnu) }
            }
        }
    }

    model.actionMessage?.let { msg ->
        AlertDialog(onDismissRequest = { model.actionMessage = null },
            confirmButton = { TextButton(onClick = { model.actionMessage = null }) { Text("OK") } },
            title = { Text("Operation") }, text = { Text(msg) })
    }
    if (showReset) ResetConfirmDialog("OLT", title, onDismiss = { showReset = false }) { scope.launch { model.reset() } }
}

@Composable
private fun OltContent(olt: OltStateDoc, openOnu: (String) -> Unit) {
    LazyColumn(Modifier.fillMaxSize().padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
        item {
            SectionCard("Status") {
                val down = olt.totalOnus - olt.onlineOnus
                KeyValueRow("ONUs", "${olt.totalOnus} · ${olt.onlineOnus} online" + if (down > 0) " · $down down" else "")
                olt.model?.let { KeyValueRow("Model", it) }
                olt.ponMode?.let { KeyValueRow("PON mode", it) }
                olt.firmwareVersion?.let { KeyValueRow("Firmware", it) }
                olt.onlineTime?.let {
                    MongoTimestamp.duration(it, olt.lastUpdate)?.let { up -> KeyValueRow("Uptime", up) }
                    KeyValueRow("Online since", MongoTimestamp.displayString(it) ?: it)
                }
            }
        }
        if (olt.rxRateBps != null || olt.txRateBps != null) {
            item {
                SectionCard("PON traffic") {
                    KeyValueRow("Downstream", "${Format.bps(olt.txRateBps)} · ${Format.percent(olt.txUtilPercent)} util")
                    KeyValueRow("Upstream", "${Format.bps(olt.rxRateBps)} · ${Format.percent(olt.rxUtilPercent)} util")
                }
            }
        }
        if (olt.asicTempC != null || olt.voltage != null) {
            item {
                SectionCard("Environment") {
                    olt.asicTempC?.let { KeyValueRow("ASIC temp", "$it °C") }
                    olt.voltage?.let { KeyValueRow("Voltage", "%.2f V".format(it)) }
                }
            }
        }
        olt.switchName?.let { sw ->
            item {
                SectionCard("Uplink") {
                    KeyValueRow("Switch", sw)
                    olt.switchPort?.let { KeyValueRow("Port", it) }
                    olt.switchAddress?.let { KeyValueRow("Address", it) }
                }
            }
        }
        olt.populatedBuckets.forEach { (state, ids) ->
            item {
                Row(verticalAlignment = Alignment.CenterVertically) {
                    StateBadge(state.label, state.color())
                    Spacer(Modifier.weight(1f))
                    Text("${ids.size}", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
                }
            }
            items(ids) { onuId ->
                ListRow(onClick = { openOnu(onuId) }) { Text(onuId) }
                HorizontalDivider()
            }
        }
    }
}
