package services
import (
"fmt"
"log"
"sync"
"time"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)
// avisosTope recuerda a qué tenants ya se les avisó en el ciclo actual, para
// no mandar un aviso por cada mensaje una vez pasado el tope. Se pierde al
// reiniciar (y entonces se vuelve a avisar una vez): aceptable para un aviso,
// y mucho más simple que una columna de estado en el tenant.
//
// ponytail: en memoria y por proceso. Si algún día corren varias réplicas,
// cada una avisa una vez — mover el flag a la BD si eso molesta.
var avisosTope struct {
sync.Mutex
ultimo map[uint]string // tenantID → "2026-08" del último aviso
}
// RegistrarUso persiste el consumo y avisa si el tenant superó el tope de su
// plan. El aviso NO corta el servicio: el agente sigue respondiendo.
func RegistrarUso(agenteID uint, tipo string, cantidad float64, unidad string) {
if agenteID == 0 || cantidad <= 0 {
return
}
models.RegistrarUsoUmind(agenteID, tipo, cantidad, unidad)
agente, err := models.GetUmindAgenteByID(agenteID)
if err != nil {
return
}
plan := models.GetPlanDeTenant(agente.TenantID)
if plan == nil || plan.TopeConsumoMensual <= 0 {
return
}
desde, hasta := cicloActual()
resumen, err := models.GetResumenUso(agente.TenantID, desde, hasta)
if err != nil {
return
}
total := 0.0
for _, r := range resumen {
total += r.Costo
}
if total < plan.TopeConsumoMensual {
return
}
avisarTopeConsumo(agente.TenantID, plan, total, desde)
}
// cicloActual devuelve el mes calendario en curso. Se usa el mes natural (no
// la fecha de alta del contrato) porque es lo que el cliente entiende al
// mirar "consumo de agosto" en su panel.
func cicloActual() (time.Time, time.Time) {
ahora := time.Now()
desde := time.Date(ahora.Year(), ahora.Month(), 1, 0, 0, 0, 0, ahora.Location())
return desde, desde.AddDate(0, 1, 0)
}
func avisarTopeConsumo(tenantID uint, plan *models.UmindPlan, total float64, desde time.Time) {
periodo := desde.Format("2006-01")
avisosTope.Lock()
if avisosTope.ultimo == nil {
avisosTope.ultimo = map[uint]string{}
}
yaAvisado := avisosTope.ultimo[tenantID] == periodo
avisosTope.ultimo[tenantID] = periodo
avisosTope.Unlock()
if yaAvisado {
return
}
tenant, err := models.GetUmindTenantByID(tenantID)
nombre := fmt.Sprintf("tenant %d", tenantID)
if err == nil {
nombre = tenant.Nombre
}
log.Printf("[UMIND_USO] %s superó el tope de consumo del plan %s: %.2f %s de %.2f",
nombre, plan.Nombre, total, plan.Moneda, plan.TopeConsumoMensual)
sendTelegramAdmin(fmt.Sprintf(
"📊 Tope de consumo superado\nCliente: %s\nPlan: %s\nConsumo del mes: %s %.2f (tope %s %.2f)\n\nEl servicio sigue activo — el excedente se cobra en el próximo ciclo.",
escapeTelegramHTML(nombre), escapeTelegramHTML(plan.Nombre),
plan.Moneda, total, plan.Moneda, plan.TopeConsumoMensual))
}