import Foundation
import Observation

@MainActor
@Observable
final class DashboardViewModel {
    var phase: LoadPhase = .idle
    var onuCounts: [(state: ONULifecycleState, count: Int)] = []
    var onuTotal = 0
    var oltCount = 0
    var controllerCount = 0
    var severityCounts: [(severity: AlarmSeverity, count: Int)] = []

    // Aggregates derived from the state docs already fetched for the counts above
    // — no extra API calls.
    var totalDownstreamBps = 0.0
    var totalUpstreamBps = 0.0
    var abnormalRxCount = 0          // ONUs with Rx optical < −28 or > −10 dBm
    var lasersOffCount = 0           // OLTs with the laser shut down
    var opticalAvailable = false     // whether the bulk ONU-STATE response carried Rx levels

    // The actual offenders, kept for drill-down (no re-fetch).
    var abnormalRxONUs: [ONUStateDoc] = []
    var lasersOffOLTs: [OLTStateDoc] = []

    private let connection: MCMSConnection
    init(connection: MCMSConnection) { self.connection = connection }

    /// Light by default: the ONU count, state breakdown, OLT traffic and
    /// laser-off count all come from the (small) OLT-STATE docs. The heavy
    /// per-ONU optical scan runs only when `extraStats` is on.
    func load(extraStats: Bool = false) async {
        phase = .loading
        do {
            // OLT-STATE drives the OLT count, ONU registration (dev guide
            // Procedure 2), total traffic, and laser state — all from a handful
            // of small docs.
            let olts = try await connection.olt.allStates()
            oltCount = olts.count

            let registration = OLTRepository.registrationMap(from: olts)
            onuTotal = registration.count
            onuCounts = Dictionary(grouping: registration.values, by: { $0 })
                .map { (state: $0.key, count: $0.value.count) }
                .sorted { $0.count > $1.count }

            totalDownstreamBps = olts.compactMap(\.txRateBps).reduce(0, +)
            totalUpstreamBps = olts.compactMap(\.rxRateBps).reduce(0, +)
            lasersOffOLTs = olts.filter(\.isLaserOff)
            lasersOffCount = lasersOffOLTs.count

            controllerCount = (try? await connection.controller.allConfigs().count) ?? 0

            if let alarms = try? await connection.onu.alarmHistories() {
                severityCounts = Dictionary(grouping: alarms, by: { $0.severity })
                    .map { (severity: $0.key, count: $0.value.count) }
                    .sorted { $0.severity.rank < $1.severity.rank }
            }

            // Opt-in heavy scan: pull every ONU-STATE for the Rx optical check.
            if extraStats {
                let onus = try await connection.onu.allStates()
                opticalAvailable = onus.contains { $0.rxOpticalDBm != nil }
                abnormalRxONUs = onus.filter { onu in
                    guard let rx = onu.rxOpticalDBm else { return false }
                    return rx < -28 || rx > -10
                }
                abnormalRxCount = abnormalRxONUs.count
            } else {
                opticalAvailable = false
                abnormalRxONUs = []
                abnormalRxCount = 0
            }
            phase = .loaded
        } catch {
            phase = .failed((error as? APIError)?.localizedDescription ?? error.localizedDescription)
        }
    }
}
