package controllers

import (
	"fmt"
	"log"

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

// ─── Webhook Bold ─────────────────────────────────────────────────────────────

// BoldWebhook recibe las notificaciones de Bold y las procesa de forma idempotente.
// La respuesta HTTP 200 se envía ANTES de cualquier lógica de negocio (requisito Bold).
func BoldWebhook(c *fiber.Ctx) error {
	rawBody := c.Body()

	// ─── 1. Responder 200 de inmediato ────────────────────────────────────────
	// Bold reintenta si no recibe 200 en < 2 segundos.
	// Fiber no permite flush parcial, así que respondemos aquí y procesamos después.
	c.Set("Content-Type", "application/json")

	// Obtener configuración activa
	cfg, err := models.GetBoldConfig()
	if err != nil {
		log.Println("[BOLD] Webhook: sin configuración activa")
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	// ─── 2. Verificar firma HMAC ─────────────────────────────────────────────
	signature := c.Get("x-bold-signature")
	if signature != "" {
		if !services.VerifyBoldSignature(rawBody, signature, cfg) {
			log.Println("[BOLD] Webhook: firma inválida — descartado")
			return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
		}
	}

	// ─── 3. Parsear evento ───────────────────────────────────────────────────
	event, err := services.ParseBoldWebhookEvent(rawBody)
	if err != nil {
		log.Printf("[BOLD] Webhook: error parseando body: %v", err)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	notificationID := event.ID
	tipo := event.Type

	// ID vacío no se puede procesar con idempotencia
	if notificationID == "" {
		log.Println("[BOLD] Webhook: notificationID vacío")
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	// ─── 4. Idempotencia: descartar duplicados ───────────────────────────────
	if models.IsBoldNotificationDuplicate(notificationID) {
		log.Printf("[BOLD] Webhook: notificación duplicada %s", notificationID)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	paymentID := event.Data.PaymentID
	if paymentID == "" {
		paymentID = event.Subject
	}
	referencia := event.Data.Metadata.Reference
	payerEmail := event.Data.PayerEmail
	monto := event.Data.Amount.Total

	// ─── 5. Guardar log del webhook ──────────────────────────────────────────
	logEntry := models.BoldWebhookLog{
		NotificationID: notificationID,
		Tipo:           tipo,
		PaymentID:      paymentID,
		Referencia:     referencia,
		PayerEmail:     payerEmail,
		Monto:          monto,
		Procesado:      false,
		Raw:            string(rawBody),
	}
	if err := models.SaveBoldWebhookLog(logEntry); err != nil {
		log.Printf("[BOLD] Webhook: error guardando log: %v", err)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	// ─── 6. Actualizar estado en callback_log para TODOS los eventos ─────────
	if referencia != "" {
		estimado := "pendiente"
		switch tipo {
		case "SALE_APPROVED":
			estimado = "pagado"
		case "SALE_REJECTED":
			estimado = "fallido"
		case "SALE_REVERSED", "CHARGEBACK":
			estimado = "revertido"
		}
		models.UpdateBoldCallbackEstado(referencia, estimado)
		if paymentID != "" {
			models.UpdateBoldCallbackEstado(paymentID, estimado)
		}
		// Enriquecer con email y monto del webhook (solo cuando hay datos reales)
		if estimado == "pagado" {
			models.EnrichBoldCallbackLog(referencia, payerEmail, monto)
		}
	}

	// Solo continuar lógica de negocio para SALE_APPROVED ─────────────────────
	if tipo != "SALE_APPROVED" {
		log.Printf("[BOLD] Webhook: tipo '%s' registrado", tipo)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	log.Printf("[BOLD] Webhook: SALE_APPROVED — payment_id=%s referencia=%s email=%s monto=%d",
		paymentID, referencia, payerEmail, monto)

	// ─── 7. Marcar contrato como pagado y limpiar enlace ────────────────────
	// La referencia tiene formato "contrato-{id}"
	if referencia != "" {
		var contratoID uint
		if _, err := fmt.Sscanf(referencia, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
			if ok, err := models.MarcarContratoPagado(contratoID); err != nil {
				log.Printf("[BOLD] Webhook: error marcando contrato %d como pagado: %v", contratoID, err)
			} else {
				log.Printf("[BOLD] Webhook: contrato %d marcado como pagado (nuevo=%v)", contratoID, ok)
				if ok {
					// Primer registro del pago: notificar SaaS + enviar correo
					go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "bold", float64(monto), "COP")
					go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
				}
			}
		}
	}

	models.MarkBoldWebhookProcessed(notificationID)

	return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
}

// ─── API: crear payment link ──────────────────────────────────────────────────

// BoldCreatePaymentLink crea un link de pago en Bold y devuelve la URL.
func BoldCreatePaymentLink(c *fiber.Ctx) error {
	type bodyReq struct {
		TotalAmount int64  `json:"total_amount"`
		Description string `json:"description"`
		PayerEmail  string `json:"payer_email"`
		Reference   string `json:"reference"`
		CallbackURL string `json:"callback_url"`
	}
	var req bodyReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.TotalAmount <= 0 || req.Description == "" || req.Reference == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "total_amount, description y reference son requeridos",
		})
	}

	cfg, err := models.GetBoldConfig()
	if err != nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
			"error": "No hay configuración activa de Bold",
		})
	}

	linkReq := services.BoldPaymentLinkRequest{
		AmountType: "CLOSE",
		Amount: services.BoldAmountField{
			Currency:    "COP",
			TotalAmount: req.TotalAmount,
		},
		Description: req.Description,
		PayerEmail:  req.PayerEmail,
		Reference:   req.Reference,
		CallbackURL: req.CallbackURL,
	}

	result, err := services.CreateBoldPaymentLink(cfg, linkReq)
	if err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
	}

	return c.JSON(fiber.Map{
		"payment_link": result.Payload.PaymentLink,
		"url":          result.Payload.URL,
	})
}

// BoldGetLinkStatus consulta el estado de un payment link existente.
func BoldGetLinkStatus(c *fiber.Ctx) error {
	linkID := c.Params("linkID")
	if linkID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "linkID requerido"})
	}

	cfg, err := models.GetBoldConfig()
	if err != nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
			"error": "No hay configuración activa de Bold",
		})
	}

	data, err := services.GetBoldPaymentLinkStatus(cfg, linkID)
	if err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"data": string(data)})
}
