package services

import (
	"fmt"
	"log"

	"github.com/robfig/cron/v3"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)

var cronScheduler *cron.Cron

// IniciarCron arranca el scheduler de tareas. Llamar desde app.go o main.go.
func IniciarCron() {
	cronScheduler = cron.New()

	// Vencimientos próximos — todos los días a las 8:00 AM
	if _, err := cronScheduler.AddFunc("0 8 * * *", ProcesarVencimientosProximos); err != nil {
		log.Printf("[CRON] Error registrando tarea vencimientos_proximo: %v", err)
		return
	}

	// Contratos ya vencidos — todos los días a las 9:00 AM
	if _, err := cronScheduler.AddFunc("0 9 * * *", ProcesarYaVencidos); err != nil {
		log.Printf("[CRON] Error registrando tarea ya_vencido: %v", err)
		return
	}

	// Polling Bold — cada 15 minutos para detectar pagos sin webhook
	if _, err := cronScheduler.AddFunc("*/15 * * * *", VerificarPagosBoldPendientes); err != nil {
		log.Printf("[CRON] Error registrando tarea bold_polling: %v", err)
		return
	}

	cronScheduler.Start()
	log.Println("[CRON] Scheduler iniciado — vencimientos próximos 8AM, ya vencidos 9AM, Bold polling cada 15min")
}

// DetenerCron para graceful shutdown
func DetenerCron() {
	if cronScheduler != nil {
		cronScheduler.Stop()
	}
}

// ProcesarVencimientos mantiene compatibilidad para llamadas manuales
func ProcesarVencimientos() {
	ProcesarVencimientosProximos()
	ProcesarYaVencidos()
}

// ProcesarVencimientosProximos procesa reglas de tipo "vencimiento_proximo"
func ProcesarVencimientosProximos() {
	log.Println("[CRON] Procesando vencimientos próximos...")

	reglas, err := models.GetReglasByTipoEvento("vencimiento_proximo")
	if err != nil {
		log.Printf("[CRON] Error obteniendo reglas vencimiento_proximo: %v", err)
		return
	}

	for _, regla := range reglas {
		contratos, err := models.GetContratosProximosVencer(regla.DiasAntes)
		if err != nil {
			log.Printf("[CRON] Error obteniendo contratos para regla %d: %v", regla.ID, err)
			continue
		}
		procesarContratos(&regla, contratos)
	}

	log.Println("[CRON] Vencimientos próximos finalizado")
}

// ProcesarYaVencidos procesa reglas de tipo "ya_vencido"
func ProcesarYaVencidos() {
	log.Println("[CRON] Procesando contratos ya vencidos...")

	reglas, err := models.GetReglasByTipoEvento("ya_vencido")
	if err != nil {
		log.Printf("[CRON] Error obteniendo reglas ya_vencido: %v", err)
		return
	}
	if len(reglas) == 0 {
		return
	}

	contratos, err := models.GetContratosYaVencidos()
	if err != nil {
		log.Printf("[CRON] Error obteniendo contratos vencidos: %v", err)
		return
	}

	for _, regla := range reglas {
		procesarContratos(&regla, contratos)
	}

	log.Println("[CRON] Ya vencidos finalizado")
}

// procesarContratos aplica filtros y envía notificaciones para una regla y lista de contratos
func procesarContratos(regla *models.NotificacionRegla, contratos []models.Contrato) {
	if len(contratos) == 0 {
		return
	}

	// Filtrar por AplicaA
	var filtrados []models.Contrato
	for _, c := range contratos {
		switch regla.AplicaA {
		case "renovable":
			for _, s := range c.Servicios {
				if s.Tipo == "renovable" {
					filtrados = append(filtrados, c)
					break
				}
			}
		case "unico":
			for _, s := range c.Servicios {
				if s.Tipo == "unico" {
					filtrados = append(filtrados, c)
					break
				}
			}
		default:
			filtrados = append(filtrados, c)
		}
	}
	if len(filtrados) == 0 {
		return
	}

	// Agrupar por cliente
	porCliente := make(map[uint][]models.Contrato)
	for _, c := range filtrados {
		porCliente[c.ClienteID] = append(porCliente[c.ClienteID], c)
	}

	for clienteID, grupoContratos := range porCliente {
		if models.YaEnviadoHoy(clienteID, regla.ID) {
			log.Printf("[CRON] Ya enviado hoy a cliente %d para regla %d — saltando", clienteID, regla.ID)
			continue
		}
		cliente := &grupoContratos[0].Cliente
		if err := EnviarNotificacionGrupo(regla, cliente, grupoContratos, ""); err != nil {
			log.Printf("[CRON] Error enviando a cliente %d: %v", clienteID, err)
		} else {
			log.Printf("[CRON] Enviado a cliente %d (%s) — %d contrato(s)", clienteID, cliente.Email, len(grupoContratos))
		}
	}
}

// VerificarPagosBoldPendientes consulta la API de Bold para cada contrato que tiene un
// enlace de pago generado pero aún no confirmado. Se ejecuta cada 15 minutos via cron.
// Esto compensa la ausencia de webhooks de Bold.
func VerificarPagosBoldPendientes() {
	contratos, err := models.GetContratosConEnlacePendiente()
	if err != nil {
		log.Printf("[BOLD-POLL] Error obteniendo contratos pendientes: %v", err)
		return
	}
	if len(contratos) == 0 {
		return
	}

	boldCfg, err := models.GetBoldConfig()
	if err != nil {
		log.Printf("[BOLD-POLL] Sin configuración Bold activa: %v", err)
		return
	}

	log.Printf("[BOLD-POLL] Verificando %d contrato(s) con enlace pendiente...", len(contratos))

	for _, c := range contratos {
		paid, paymentID, monto, err := CheckBoldLinkPaid(boldCfg, c.EnlacePagoLinkID)
		if err != nil {
			log.Printf("[BOLD-POLL] Error consultando link %s (contrato %d): %v", c.EnlacePagoLinkID, c.ID, err)
			continue
		}
		if !paid {
			continue
		}

		ref := fmt.Sprintf("contrato-%d", c.ID)
		log.Printf("[BOLD-POLL] Pago detectado para %s (payment_id=%s, monto=%d)", ref, paymentID, monto)

		if ok, _ := models.MarcarContratoPagado(c.ID); ok {
			go EnviarCorreoConfirmacionPago(c.ID, "bold")
			log.Printf("[BOLD-POLL] Contrato %d marcado como pagado", c.ID)
		}

		// Enriquecer callback log si existe
		go models.EnrichBoldCallbackLog(ref, c.Cliente.Email, monto)

		// Guardar en webhook log como API_CHECK para trazabilidad
		notifID := "api-check-" + paymentID
		if paymentID != "" && !models.IsBoldNotificationDuplicate(notifID) {
			_ = models.SaveBoldWebhookLog(models.BoldWebhookLog{
				NotificationID: notifID,
				Tipo:           "API_CHECK",
				PaymentID:      paymentID,
				Referencia:     ref,
				PayerEmail:     c.Cliente.Email,
				Monto:          monto,
				Procesado:      true,
			})
		}
	}

	log.Println("[BOLD-POLL] Verificación completada")
}
