package controllers

import (
	"fmt"
	"log"
	"strconv"
	"time"

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

// Función para manejar la solicitud de creación de un plan
func CreatePlan(c *fiber.Ctx) error {
	var plan models.PlanRequest

	// Parsear el cuerpo de la solicitud a la estructura PlanRequest
	if err := c.BodyParser(&plan); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al parsear el cuerpo de la solicitud: %v", err),
		})
	}

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para crear el plan
	response, err := services.CreatePlan(*dlocalConfig, plan)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al crear el plan: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Plan creado exitosamente.",
		"response": string(response),
	})
}

func SeePlan(c *fiber.Ctx) error {
	planID := c.Params("planID")

	// Verifica si se recibió correctamente
	if planID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "PlanID es requerido.",
		})
	}

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para ver el plan
	response, err := services.SeePlan(*dlocalConfig, planID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al obtener el plan: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Plan obtenido exitosamente.",
		"response": string(response),
	})
}

func UpdatedPlan(c *fiber.Ctx) error {
	planID := c.Params("planID")
	var plan services.PlanUpdated
	// Parsear el cuerpo de la solicitud a la estructura PlanRequest
	if err := c.BodyParser(&plan); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al parsear el cuerpo de la solicitud: %v", err),
		})
	}

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para crear el plan
	response, err := services.UpdatedPlan(*dlocalConfig, plan, planID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al crear el plan: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Plan actualizado exitosamente.",
		"response": string(response),
	})
}

func DeactivatePlan(c *fiber.Ctx) error {
	planID := c.Params("planID")
	subscriptionId := c.Params("subscriptionId")

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para crear el plan
	response, err := services.DeactivatePlan(*dlocalConfig, planID, subscriptionId)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al crear el plan: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Suscripción cancelada exitosamente.",
		"response": string(response),
	})
}

func SeePlanes(c *fiber.Ctx) error {

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para ver el plan
	response, err := services.SeePlanes(*dlocalConfig)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al obtener los planes: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Planes obtenido exitosamente.",
		"response": string(response),
	})
}
func SeeSubscription(c *fiber.Ctx) error {
	subscriptionId := c.Params("subscriptionId")
	invoiceId := c.Params("invoiceId")

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para ver suscripción
	response, err := services.SeeSubscription(*dlocalConfig, subscriptionId, invoiceId)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al obtener la suscripción: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Suscripción obtenida exitosamente.",
		"response": string(response),
	})
}

func CreatePago(c *fiber.Ctx) error {
	var pago services.PagoRequest

	// Parsear el cuerpo de la solicitud a la estructura PagoRequest
	if err := c.BodyParser(&pago); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al parsear el cuerpo de la solicitud: %v", err),
		})
	}

	// Obtener la configuración activa de DlocalApi
	dlocalConfig, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al obtener la configuración activa de DlocalApi.",
		})
	}

	// Llamar al servicio para crear el pago
	response, err := services.CreatePago(*dlocalConfig, pago)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": fmt.Sprintf("Error al crear el pago: %v", err),
		})
	}

	// Devolver la respuesta exitosa
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message":  "Pago creado exitosamente.",
		"response": string(response),
	})
}

// ─── Webhook dLocal ───────────────────────────────────────────────────────────

// DlocalWebhook recibe notificaciones de pago de dLocal y las registra de forma idempotente.
// dLocal espera HTTP 200; se responde inmediatamente y se procesa a continuación.
func DlocalWebhook(c *fiber.Ctx) error {
	rawBody := c.Body()

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

	// ─── 2. Verificar firma HMAC-SHA256 (opcional según config) ──────────────
	signature := c.Get("X-dLocal-Signature")
	if signature != "" {
		if !services.VerifyDlocalSignature(rawBody, signature, *cfg) {
			log.Println("[DLOCAL] Webhook: firma inválida — descartado")
			return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
		}
	}

	// ─── 3. Parsear notificación ─────────────────────────────────────────────
	notif, err := services.ParseDlocalWebhookNotification(rawBody)
	if err != nil {
		log.Printf("[DLOCAL] Webhook: error parseando body: %v", err)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	notificationID := notif.ID
	if notificationID == "" {
		log.Println("[DLOCAL] Webhook: notification_id vacío")
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

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

	// ─── 5. Extraer campos (pago directo o suscripción) ──────────────────────
	paymentID := notif.Payment.ID
	orderID := notif.Payment.OrderID
	payerEmail := notif.Payment.Payer.Email
	monto := notif.Payment.Amount
	moneda := notif.Payment.Currency
	tipo := notif.Type
	estado := notif.Status

	// Si es suscripción, tomar amounts del invoice
	if tipo == "SUBSCRIPTION_CHARGE" || tipo == "SUBSCRIPTION_PAYMENT" {
		if notif.Invoice.Amount > 0 {
			monto = notif.Invoice.Amount
			moneda = notif.Invoice.Currency
		}
		if paymentID == "" {
			paymentID = notif.Invoice.ID
		}
		if orderID == "" {
			orderID = notif.Subscription.ID
		}
	}

	// ─── 6. Guardar log ──────────────────────────────────────────────────────
	logEntry := models.DlocalPaymentLog{
		NotificationID: notificationID,
		Fuente:         "webhook",
		Tipo:           tipo,
		Estado:         estado,
		PaymentID:      paymentID,
		OrderID:        orderID,
		Referencia:     orderID, // order_id coincide con la referencia del contrato
		PayerEmail:     payerEmail,
		Monto:          monto,
		Moneda:         moneda,
		Procesado:      false,
		Raw:            string(rawBody),
	}
	if err := models.SaveDlocalPaymentLog(logEntry); err != nil {
		log.Printf("[DLOCAL] Webhook: error guardando log: %v", err)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	// ─── 7. Procesar si el estado es PAID ────────────────────────────────────
	if estado != "PAID" {
		log.Printf("[DLOCAL] Webhook: estado '%s' — registrado sin procesar", estado)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
	}

	log.Printf("[DLOCAL] Webhook: PAID — payment_id=%s order=%s email=%s monto=%.2f %s",
		paymentID, orderID, payerEmail, monto, moneda)

	// Intentar vincular con contrato si el order_id tiene formato "contrato-{id}"
	if orderID != "" {
		var contratoID uint
		if _, err := fmt.Sscanf(orderID, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
			if ok, err := models.MarcarContratoPagado(contratoID); err != nil {
				log.Printf("[DLOCAL] Webhook: error marcando contrato %d como pagado: %v", contratoID, err)
			} else {
				log.Printf("[DLOCAL] Webhook: contrato %d marcado como pagado (nuevo=%v)", contratoID, ok)
				if ok {
					go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "dlocal", monto, moneda)
					go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
				}
			}
		}
	}

	models.MarkDlocalPaymentProcessed(notificationID)

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

// ─── Registro manual de pago dLocal ──────────────────────────────────────────

// DlocalRegistrarPago permite registrar manualmente un pago dLocal desde el backoffice
// cuando la notificación no llegó por webhook (actualización directa, confirmación manual, etc.).
func DlocalRegistrarPago(c *fiber.Ctx) error {
	type body struct {
		PaymentID  string  `json:"payment_id"`
		OrderID    string  `json:"order_id"`
		Referencia string  `json:"referencia"`
		PayerEmail string  `json:"payer_email"`
		Monto      float64 `json:"monto"`
		Moneda     string  `json:"moneda"`
		Estado     string  `json:"estado"` // PAID, PENDING, REJECTED, …
		Tipo       string  `json:"tipo"`   // PAYMENT, SUBSCRIPTION_CHARGE, manual, …
		Nota       string  `json:"nota"`
	}
	var b body
	if err := c.BodyParser(&b); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if b.Estado == "" {
		b.Estado = "PAID"
	}
	if b.Tipo == "" {
		b.Tipo = "manual"
	}
	if b.Moneda == "" {
		b.Moneda = "USD"
	}

	// Generar un notification_id único para entradas manuales
	notifID := "manual-" + strconv.FormatInt(time.Now().UnixNano(), 36)
	if b.PaymentID != "" {
		notifID = "manual-" + b.PaymentID
	}

	// No deduplicar entradas manuales con el mismo payment_id — pueden ser actualizaciones
	// Se usa prefijo diferente por cada llamada si payment_id es el mismo
	if models.IsDlocalNotificationDuplicate(notifID) {
		notifID = "manual-" + strconv.FormatInt(time.Now().UnixNano(), 36)
	}

	referencia := b.Referencia
	if referencia == "" {
		referencia = b.OrderID
	}

	entry := models.DlocalPaymentLog{
		NotificationID: notifID,
		Fuente:         "manual",
		Tipo:           b.Tipo,
		Estado:         b.Estado,
		PaymentID:      b.PaymentID,
		OrderID:        b.OrderID,
		Referencia:     referencia,
		PayerEmail:     b.PayerEmail,
		Monto:          b.Monto,
		Moneda:         b.Moneda,
		Nota:           b.Nota,
		Procesado:      b.Estado == "PAID",
	}
	if err := models.SaveDlocalPaymentLog(entry); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	// Si el estado es PAID y referencia tiene formato "contrato-{id}", marcar contrato
	if b.Estado == "PAID" && referencia != "" {
		var contratoID uint
		if _, err := fmt.Sscanf(referencia, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
			if _, markErr := models.MarcarContratoPagado(contratoID); markErr != nil {
				log.Printf("[DLOCAL] Registro manual: error marcando contrato %d: %v", contratoID, markErr)
			} else {
				log.Printf("[DLOCAL] Registro manual: contrato %d marcado como pagado", contratoID)
			}
		}
	}

	return c.JSON(fiber.Map{"message": "Pago registrado", "notification_id": notifID})
}
