package no.svorka.mcms.ui

import androidx.compose.foundation.clickable
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.ChevronRight
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
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.delay
import no.svorka.mcms.core.*

class DashboardModel(private val connection: McmsConnection) {
    var phase by mutableStateOf<LoadPhase>(LoadPhase.Idle)
    var onuTotal by mutableStateOf(0)
    var oltCount by mutableStateOf(0)
    var controllerCount by mutableStateOf(0)
    var onuCounts by mutableStateOf<List<Pair<OnuLifecycleState, Int>>>(emptyList())
    var severityCounts by mutableStateOf<List<Pair<AlarmSeverity, Int>>>(emptyList())
    var totalDownBps by mutableStateOf(0.0)
    var totalUpBps by mutableStateOf(0.0)
    var abnormalRx by mutableStateOf<List<OnuStateDoc>>(emptyList())
    var lasersOff by mutableStateOf<List<OltStateDoc>>(emptyList())
    var opticalAvailable by mutableStateOf(false)

    // Monotonic load id: a slower earlier load must not overwrite a newer one's data.
    private var loadGen = 0

    suspend fun load(extraStats: Boolean) {
        val gen = ++loadGen
        phase = LoadPhase.Loading
        try {
            // Fetch into locals, then commit atomically once we confirm we're still current.
            val olts = connection.olt.allStates()
            val reg = OltRepository.registrationMap(olts)
            val counts = reg.values.groupingBy { it }.eachCount().toList().sortedByDescending { it.second }
            val down = olts.mapNotNull { it.txRateBps }.sum()
            val up = olts.mapNotNull { it.rxRateBps }.sum()
            val lasers = olts.filter { it.isLaserOff }
            val ctrl = runCatching { connection.controller.allConfigs().size }.getOrDefault(0)
            val alarms = runCatching { connection.onu.alarmHistories() }.getOrDefault(emptyList())
            val sev = alarms.groupingBy { it.severity }.eachCount().toList().sortedBy { it.first.rank }
            val (optAvail, abnormal) = if (extraStats) {
                val onus = connection.onu.allStates()
                onus.any { it.rxOpticalDBm != null } to
                    onus.filter { val rx = it.rxOpticalDBm; rx != null && (rx < -28 || rx > -10) }
            } else {
                false to emptyList<OnuStateDoc>()
            }
            if (gen != loadGen) return   // a newer load superseded us — drop stale results
            oltCount = olts.size
            onuTotal = reg.size
            onuCounts = counts
            totalDownBps = down
            totalUpBps = up
            lasersOff = lasers
            controllerCount = ctrl
            severityCounts = sev
            opticalAvailable = optAvail
            abnormalRx = abnormal
            phase = LoadPhase.Loaded
        } catch (e: CancellationException) {
            throw e   // cancellation is not a failure
        } catch (e: Throwable) {
            if (gen == loadGen) phase = LoadPhase.Failed(e.uiMessage())
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DashboardScreen(
    connection: McmsConnection,
    openAbnormalRx: (List<OnuStateDoc>) -> Unit,
    openLaserOff: (List<OltStateDoc>) -> Unit,
) {
    val model = remember(connection) { DashboardModel(connection) }
    var extraStats by rememberSaveable { mutableStateOf(false) }
    var autoRefresh by rememberSaveable { mutableStateOf(false) }
    var menuOpen by remember { mutableStateOf(false) }

    LaunchedEffect(extraStats, autoRefresh) {
        while (true) {
            model.load(extraStats)
            if (!autoRefresh) break
            delay(30_000)
        }
    }

    Scaffold(topBar = {
        TopAppBar(
            title = { Text("Dashboard") },
            actions = {
                IconButton(onClick = { menuOpen = true }) { Icon(Icons.Filled.MoreVert, "Options") }
                DropdownMenu(expanded = menuOpen, onDismissRequest = { menuOpen = false }) {
                    DropdownMenuItem(
                        text = { Text("Detailed stats (Rx scan)") },
                        trailingIcon = { Switch(checked = extraStats, onCheckedChange = { extraStats = it }) },
                        onClick = { extraStats = !extraStats },
                    )
                    DropdownMenuItem(
                        text = { Text("Auto-refresh · 30s") },
                        trailingIcon = { Switch(checked = autoRefresh, onCheckedChange = { autoRefresh = it }) },
                        onClick = { autoRefresh = !autoRefresh },
                    )
                }
            },
        )
    }) { padding ->
        Box(Modifier.padding(padding).fillMaxSize()) {
            when (val p = model.phase) {
                is LoadPhase.Idle, is LoadPhase.Loading -> if (model.onuTotal == 0 && model.oltCount == 0) LoadingView("Loading network…") else content(model, openAbnormalRx, openLaserOff)
                // Keep showing the last good data if a background refresh fails.
                is LoadPhase.Failed -> if (model.onuTotal == 0 && model.oltCount == 0) ErrorState(p.message) else content(model, openAbnormalRx, openLaserOff)
                is LoadPhase.Loaded -> content(model, openAbnormalRx, openLaserOff)
            }
        }
    }
}

@Composable
private fun content(
    model: DashboardModel,
    openAbnormalRx: (List<OnuStateDoc>) -> Unit,
    openLaserOff: (List<OltStateDoc>) -> Unit,
) {
    LazyColumn(
        Modifier.fillMaxSize().padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(20.dp),
    ) {
        item {
            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                CountTile("ONUs", model.onuTotal, Modifier.weight(1f))
                CountTile("OLTs", model.oltCount, Modifier.weight(1f))
                CountTile("Controllers", model.controllerCount, Modifier.weight(1f))
            }
        }
        if (model.totalDownBps > 0 || model.totalUpBps > 0) {
            item {
                SectionCard("Traffic · all OLTs") {
                    Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                        Column {
                            Text("↓ Downstream", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
                            Text(Format.bps(model.totalDownBps), style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
                        }
                        Column(horizontalAlignment = Alignment.End) {
                            Text("↑ Upstream", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
                            Text(Format.bps(model.totalUpBps), style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
                        }
                    }
                }
            }
        }
        if (model.opticalAvailable || model.lasersOff.isNotEmpty()) {
            item {
                SectionCard("Health") {
                    if (model.opticalAvailable) {
                        HealthRow("ONUs with abnormal Rx", "< −28 or > −10 dBm", model.abnormalRx.size) {
                            if (model.abnormalRx.isNotEmpty()) openAbnormalRx(model.abnormalRx)
                        }
                    }
                    HealthRow("OLTs with laser off", null, model.lasersOff.size) {
                        if (model.lasersOff.isNotEmpty()) openLaserOff(model.lasersOff)
                    }
                }
            }
        }
        if (model.onuCounts.isNotEmpty()) {
            item {
                SectionCard("ONU states") {
                    model.onuCounts.forEach { (state, count) ->
                        Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) {
                            StateBadge(state.label, state.color())
                            Spacer(Modifier.weight(1f))
                            Text("$count")
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun HealthRow(title: String, detail: String?, count: Int, onClick: () -> Unit) {
    Row(
        Modifier.fillMaxWidth().clickable(enabled = count > 0, onClick = onClick).padding(vertical = 6.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Column(Modifier.weight(1f)) {
            Text(title)
            if (detail != null) Text(detail, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
        }
        Text("$count", fontWeight = FontWeight.SemiBold, color = if (count > 0) Color(0xFFEF6C00) else Color(0xFF2E7D32))
        if (count > 0) Icon(Icons.Filled.ChevronRight, null, tint = MaterialTheme.colorScheme.outline)
    }
}

// ── Drill-down screens ──

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AbnormalRxScreen(onus: List<OnuStateDoc>, onBack: () -> Unit, openOnu: (String) -> Unit) {
    Scaffold(topBar = { TopAppBar(title = { Text("Abnormal Rx") }, navigationIcon = { BackButton(onBack) }) }) { padding ->
        LazyColumn(Modifier.padding(padding).fillMaxSize()) {
            items(onus) { onu ->
                ListRow(onClick = { openOnu(onu.id) }) {
                    Column(Modifier.weight(1f)) {
                        Text(onu.displayName, fontWeight = FontWeight.Medium)
                        onu.parentOlt?.let { Text("OLT $it", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) }
                    }
                    onu.rxOpticalDBm?.let { rx ->
                        Column(horizontalAlignment = Alignment.End) {
                            Text("${"%.1f".format(rx)} dBm", color = Color(0xFFEF6C00))
                            Text(if (rx < -28) "weak" else "strong", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
                        }
                    }
                }
                HorizontalDivider()
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LaserOffScreen(olts: List<OltStateDoc>, onBack: () -> Unit, openOlt: (String, String) -> Unit) {
    Scaffold(topBar = { TopAppBar(title = { Text("Laser off") }, navigationIcon = { BackButton(onBack) }) }) { padding ->
        LazyColumn(Modifier.padding(padding).fillMaxSize()) {
            items(olts) { olt ->
                ListRow(onClick = { openOlt(olt.id, olt.displayName) }) {
                    Column(Modifier.weight(1f)) {
                        Text(olt.displayName, fontWeight = FontWeight.Medium)
                        Text(olt.laserShutdown ?: "Laser off", style = MaterialTheme.typography.bodySmall, color = Color(0xFFEF6C00))
                    }
                }
                HorizontalDivider()
            }
        }
    }
}
