package com.ffii.fpsms.modules.monitoring.web

import com.ffii.core.exception.BadRequestException
import com.ffii.fpsms.modules.common.SecurityUtils
import com.ffii.fpsms.modules.monitoring.service.ClientPresenceService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import jakarta.servlet.http.HttpServletRequest
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeParseException

/** Always registered so client heartbeats work whenever the frontend reporter is enabled. */
@RestController
@RequestMapping("/device-presence")
class ClientPresenceController(
    private val clientPresenceService: ClientPresenceService,
) {

    data class HeartbeatBody(
        val deviceId: String? = null,
        val displayName: String? = null,
        val currentPath: String? = null,
        val rttMs: Int? = null,
        val connectionQuality: String? = null,
        val navigatorOnline: Boolean? = true,
        val userAgent: String? = null,
        val clientType: String? = null,
        val activityBump: Boolean? = true,
    )

    @GetMapping("/ping")
    fun ping(): ResponseEntity<Map<String, Any>> {
        return ResponseEntity.ok(mapOf("ok" to true, "ts" to System.currentTimeMillis()))
    }

    @PostMapping("/heartbeat")
    fun heartbeat(@RequestBody body: HeartbeatBody, request: HttpServletRequest): ResponseEntity<Map<String, Any>> {
        val user = SecurityUtils.getUser().orElseThrow { BadRequestException("Not authenticated") }
        val deviceId = body.deviceId?.trim().orEmpty()
        if (deviceId.isEmpty()) {
            throw BadRequestException("deviceId is required")
        }
        val ua = body.userAgent?.takeIf { it.isNotBlank() }
            ?: run {
                // allow server-side only if client omits; usually sent from browser
                null
            }
        clientPresenceService.upsertHeartbeat(
            user,
            ClientPresenceService.HeartbeatRequest(
                deviceId = deviceId,
                displayName = body.displayName,
                currentPath = body.currentPath,
                rttMs = body.rttMs,
                connectionQuality = body.connectionQuality,
                navigatorOnline = body.navigatorOnline,
                userAgent = ua,
                clientType = body.clientType,
                clientIp = ClientIpResolver.resolve(request),
                activityBump = body.activityBump != false,
            ),
        )
        return ResponseEntity.ok(mapOf("ok" to true))
    }

    @GetMapping("/history")
    fun listHistory(
        @RequestParam fromDateTime: String,
        @RequestParam toDateTime: String,
        @RequestParam(required = false) clientType: String?,
        @RequestParam(required = false) deviceId: String?,
        @RequestParam(required = false) status: String?,
    ): ResponseEntity<Any> {
        val from = parseDateTimeParam(fromDateTime, endOfDay = false)
            ?: return ResponseEntity.badRequest().body(mapOf("error" to "Invalid fromDateTime"))
        val to = parseDateTimeParam(toDateTime, endOfDay = true)
            ?: return ResponseEntity.badRequest().body(mapOf("error" to "Invalid toDateTime"))
        if (from.isAfter(to)) {
            return ResponseEntity.badRequest().body(mapOf("error" to "fromDateTime must be before toDateTime"))
        }
        val statuses = status
            ?.split(",")
            ?.map { it.trim().lowercase() }
            ?.filter { it.isNotBlank() }
            ?.takeIf { it.isNotEmpty() }
        return ResponseEntity.ok(
            clientPresenceService.listHistory(from, to, clientType, deviceId, statuses),
        )
    }

    @GetMapping("/active")
    fun listActive(
        @RequestParam(required = false) clientType: String?,
    ): ResponseEntity<Map<String, Any>> {
        val clients = clientPresenceService.listActive(clientType)
        val offlineCount = clients.count { it["status"] == "offline" }
        val poorCount = clients.count { it["status"] == "poor" }
        return ResponseEntity.ok(
            mapOf(
                "clients" to clients,
                "summary" to mapOf(
                    "total" to clients.size,
                    "offline" to offlineCount,
                    "poor" to poorCount,
                    "online" to clients.count { it["status"] == "online" },
                    "idle" to clients.count { it["status"] == "idle" },
                ),
            ),
        )
    }

    private fun parseDateTimeParam(raw: String, endOfDay: Boolean): LocalDateTime? {
        val text = raw.trim()
        if (text.isEmpty()) return null
        return try {
            when {
                text.length <= 10 -> {
                    val d = LocalDate.parse(text.take(10))
                    if (endOfDay) d.atTime(23, 59, 59) else d.atStartOfDay()
                }
                else -> LocalDateTime.parse(text.replace(" ", "T").take(19))
            }
        } catch (_: DateTimeParseException) {
            null
        }
    }
}
