package controllers

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)

type PasoDiagnostico struct {
	Paso    string `json:"paso"`
	OK      bool   `json:"ok"`
	Mensaje string `json:"mensaje"`
	Ms      int64  `json:"ms"`
}

// EjecutarTestCiclo ejecuta el ciclo completo de notificaciones sobre datos temporales:
//  1. Envío de bienvenida
//  2. Marcar contrato como pagado + enviar confirmación de pago
//  3. Idempotencia (no reenviar si ya se envió hoy)
//
// Crea y destruye los datos de prueba dentro de la misma transacción de limpieza.
// POST /app/api/diagnostico/ciclo-notificaciones
func EjecutarTestCiclo(c *fiber.Ctx) error {
	var body struct {
		TestEmail string `json:"test_email"`
	}
	if err := c.BodyParser(&body); err != nil || body.TestEmail == "" {
		return c.Status(400).JSON(fiber.Map{"error": "test_email requerido"})
	}

	db := app.Http.Database.DB
	suffix := fmt.Sprintf("_diag_%d", time.Now().UnixNano())
	pasos := []PasoDiagnostico{}
	allOK := true

	paso := func(nombre string, fn func() error) {
		t0 := time.Now()
		err := fn()
		ms := time.Since(t0).Milliseconds()
		ok := err == nil
		msg := "OK"
		if !ok {
			msg = err.Error()
			allOK = false
		}
		pasos = append(pasos, PasoDiagnostico{Paso: nombre, OK: ok, Mensaje: msg, Ms: ms})
	}

	// ── Datos temporales ──────────────────────────────────────────────────────
	var plantBienvenida, plantPago models.PlantillaCorreo
	var reglaBienvenida, reglaPago models.NotificacionRegla
	var cliente models.Cliente
	var contrato models.Contrato

	// ── Cleanup siempre al finalizar ──────────────────────────────────────────
	defer func() {
		if contrato.ID > 0 {
			db.Unscoped().Where("cliente_id = ?", cliente.ID).Delete(&models.NotificacionLog{})
			db.Unscoped().Delete(&contrato)
		}
		if cliente.ID > 0 {
			db.Unscoped().Delete(&cliente)
		}
		if reglaBienvenida.ID > 0 {
			db.Unscoped().Delete(&reglaBienvenida)
		}
		if reglaPago.ID > 0 {
			db.Unscoped().Delete(&reglaPago)
		}
		if plantBienvenida.ID > 0 {
			db.Unscoped().Delete(&plantBienvenida)
		}
		if plantPago.ID > 0 {
			db.Unscoped().Delete(&plantPago)
		}
	}()

	// ── SETUP: crear datos de prueba ──────────────────────────────────────────
	paso("setup: plantilla bienvenida", func() error {
		plantBienvenida = models.PlantillaCorreo{
			Nombre:     "DIAG Bienvenida" + suffix,
			Asunto:     "[TEST] Bienvenido al sistema",
			CuerpoHTML: "<p>Hola {{.ClienteNombre}}, bienvenido. Tu vencimiento es {{.FechaVencimiento}}.</p>",
			Tipo:       "bienvenida",
		}
		return db.Create(&plantBienvenida).Error
	})

	paso("setup: plantilla pago confirmado", func() error {
		plantPago = models.PlantillaCorreo{
			Nombre:     "DIAG Pago Confirmado" + suffix,
			Asunto:     "[TEST] Tu pago fue recibido",
			CuerpoHTML: "<p>Hola {{.ClienteNombre}}, recibimos tu pago de ${{.Total}}. ¡Gracias!</p>",
			Tipo:       "pago",
		}
		return db.Create(&plantPago).Error
	})

	paso("setup: regla bienvenida", func() error {
		if plantBienvenida.ID == 0 {
			return fmt.Errorf("plantilla bienvenida no creada")
		}
		reglaBienvenida = models.NotificacionRegla{
			Nombre:         "DIAG Regla Bienvenida" + suffix,
			TipoEvento:     "bienvenida",
			PlantillaID:    plantBienvenida.ID,
			Activo:         true,
			AplicaA:        "todos",
			PasarelaEnlace: "ninguna",
		}
		return db.Create(&reglaBienvenida).Error
	})

	paso("setup: regla pago_recibido", func() error {
		if plantPago.ID == 0 {
			return fmt.Errorf("plantilla pago no creada")
		}
		reglaPago = models.NotificacionRegla{
			Nombre:         "DIAG Regla Pago" + suffix,
			TipoEvento:     "pago_recibido",
			PlantillaID:    plantPago.ID,
			Activo:         true,
			AplicaA:        "todos",
			PasarelaEnlace: "ninguna",
		}
		return db.Create(&reglaPago).Error
	})

	paso("setup: cliente y contrato temporales", func() error {
		cliente = models.Cliente{
			Nombre:  "DIAG Cliente" + suffix,
			Email:   body.TestEmail,
			Empresa: "Diagnóstico Sistema",
			Activo:  true,
		}
		if err := db.Create(&cliente).Error; err != nil {
			return err
		}
		now := time.Now()
		contrato = models.Contrato{
			ClienteID:        cliente.ID,
			Cliente:          cliente,
			FechaInicio:      now,
			FechaVencimiento: now.AddDate(1, 0, 0),
			PrecioAcordado:   99.00,
			Estado:           "activo",
		}
		if err := db.Create(&contrato).Error; err != nil {
			return err
		}
		contrato.Cliente = cliente
		return nil
	})

	// Si el setup falló, no continuar con los pasos funcionales
	if !allOK {
		return c.JSON(fiber.Map{"pasos": pasos, "ok": false, "resumen": "Setup falló — ver pasos anteriores"})
	}

	// ── PASO 1: Enviar correo de bienvenida ───────────────────────────────────
	paso("paso 1 — envío de bienvenida", func() error {
		return services.EnviarNotificacionGrupo(&reglaBienvenida, &cliente, []models.Contrato{contrato}, "")
	})

	// Verificar log en DB
	paso("paso 1 — verificar log en historial", func() error {
		var count int64
		db.Model(&models.NotificacionLog{}).
			Where("cliente_id = ? AND regla_id = ? AND estado = 'enviado'", cliente.ID, reglaBienvenida.ID).
			Count(&count)
		if count == 0 {
			return fmt.Errorf("no se encontró log con estado='enviado' para la regla bienvenida")
		}
		return nil
	})

	// ── PASO 2: Simular recepción de pago ─────────────────────────────────────
	paso("paso 2 — marcar contrato como pagado", func() error {
		_, err := models.MarcarContratoPagado(contrato.ID)
		return err
	})

	paso("paso 2 — verificar pago_confirmado en DB", func() error {
		var c models.Contrato
		db.Select("pago_confirmado, fecha_pago").First(&c, contrato.ID)
		if !c.PagoConfirmado {
			return fmt.Errorf("pago_confirmado sigue siendo false tras MarcarContratoPagado")
		}
		if c.FechaPago == nil {
			return fmt.Errorf("fecha_pago es nil tras MarcarContratoPagado")
		}
		return nil
	})

	paso("paso 2 — envío de confirmación de pago", func() error {
		// EnviarCorreoConfirmacionPago es void — usar EnviarNotificacionGrupo directamente
		// para capturar el error (la función void loguea pero no retorna)
		reloadContrato, err := models.GetContratoByID(contrato.ID)
		if err != nil {
			return err
		}
		return services.EnviarNotificacionGrupo(&reglaPago, &cliente, []models.Contrato{*reloadContrato}, "")
	})

	paso("paso 2 — verificar log de pago en historial", func() error {
		var count int64
		db.Model(&models.NotificacionLog{}).
			Where("cliente_id = ? AND regla_id = ? AND estado = 'enviado'", cliente.ID, reglaPago.ID).
			Count(&count)
		if count == 0 {
			return fmt.Errorf("no se encontró log con estado='enviado' para la regla pago_recibido")
		}
		return nil
	})

	// ── PASO 3: Idempotencia ──────────────────────────────────────────────────
	paso("paso 3 — idempotencia (no reenviar si ya enviado hoy)", func() error {
		// Verificar que YaEnviadoHoy retorna true para ambas reglas
		if !models.YaEnviadoHoy(cliente.ID, reglaBienvenida.ID) {
			return fmt.Errorf("YaEnviadoHoy debería ser true para bienvenida, pero retornó false")
		}
		if !models.YaEnviadoHoy(cliente.ID, reglaPago.ID) {
			return fmt.Errorf("YaEnviadoHoy debería ser true para pago_recibido, pero retornó false")
		}
		return nil
	})

	// Contar emails en historial para el resumen
	var totalLogs int64
	db.Model(&models.NotificacionLog{}).Where("cliente_id = ?", cliente.ID).Count(&totalLogs)

	// Serializar IDs para debug
	idsJSON, _ := json.Marshal(map[string]uint{
		"cliente_id":           cliente.ID,
		"contrato_id":          contrato.ID,
		"regla_bienvenida_id":  reglaBienvenida.ID,
		"regla_pago_id":        reglaPago.ID,
		"plantilla_bienvenida": plantBienvenida.ID,
		"plantilla_pago":       plantPago.ID,
	})

	resumen := fmt.Sprintf("Ciclo completado — %d emails enviados al historial, destinatario: %s",
		totalLogs, body.TestEmail)
	if !allOK {
		resumen = "Ciclo con errores — revisar pasos"
	}

	return c.JSON(fiber.Map{
		"pasos":   pasos,
		"ok":      allOK,
		"resumen": resumen,
		"ids_tmp": string(idsJSON),
	})
}
