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"
)

// PagoExitosoPage renderiza la página pública de confirmación de pago.
// Bold redirige al cliente aquí tras el pago. Registra el intento en bold_callback_log.
func PagoExitosoPage(c *fiber.Ctx) error {
	// Recolectar todos los parámetros posibles que Bold puede enviar
	params := map[string]string{}
	// Parámetros clave que Bold envía en la callback_url
	for _, k := range []string{"bold-order-id", "order_id", "payment_link", "reference", "id", "ref"} {
		if v := c.Query(k, ""); v != "" {
			params[k] = v
		}
	}
	// bold-tx-status: Bold envía el resultado directamente en la URL
	if v := c.Query("bold-tx-status", ""); v != "" {
		params["bold-tx-status"] = v
	}
	c.Request().URI().QueryArgs().VisitAll(func(k, v []byte) {
		params[string(k)] = string(v)
	})

	// Resolver la referencia principal (orden de prioridad)
	ref := ""
	for _, k := range []string{"ref", "reference", "bold-order-id", "payment_link", "order_id", "id"} {
		if v := params[k]; v != "" {
			ref = v
			break
		}
	}
	paymentLink := params["payment_link"]

	// Serializar todos los params para auditoría
	paramsJSON := "{"
	for k, v := range params {
		paramsJSON += `"` + k + `":"` + v + `",`
	}
	if len(paramsJSON) > 1 {
		paramsJSON = paramsJSON[:len(paramsJSON)-1]
	}
	paramsJSON += "}"

	// Si Bold ya envía el resultado en la URL (bold-tx-status), usarlo directamente
	// antes de hacer cualquier llamada a la API externa.
	boldTxStatus := params["bold-tx-status"]
	if boldTxStatus == "rejected" || boldTxStatus == "cancelled" || boldTxStatus == "failed" {
		_ = models.SaveBoldCallbackLog(models.BoldCallbackLog{
			Referencia: ref, PaymentLink: paymentLink, Params: paramsJSON,
			Estado: "fallido", IP: c.IP(), UserAgent: string(c.Request().Header.UserAgent()),
		})
		return c.Render("pago_exitoso", fiber.Map{
			"Ref": ref, "Estado": "rechazado", "FechaPago": "",
		}, "layouts/landing")
	}

	// Bold confirmó el pago directamente en la URL de retorno
	if boldTxStatus == "approved" {
		// IP real (detrás de nginx)
		ip := c.Get("X-Real-IP")
		if ip == "" {
			ip = c.Get("X-Forwarded-For")
		}
		if ip == "" {
			ip = c.IP()
		}
		_ = models.SaveBoldCallbackLog(models.BoldCallbackLog{
			Referencia: ref, PaymentLink: paymentLink, Params: paramsJSON,
			Estado: "pagado", IP: ip, UserAgent: string(c.Request().Header.UserAgent()),
		})
		// Marcar contrato si aún no está confirmado
		var contratoID uint
		if _, err := fmt.Sscanf(ref, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
			if ok, _ := models.MarcarContratoPagado(contratoID); ok {
				go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
			}
			// Enriquecer callback log: primero webhook log, luego Bold API directa
			go func(linkID string) {
				// 1. Si el webhook ya llegó, usar esos datos
				if wLogs, err := models.GetBoldWebhookLogsByRef(ref); err == nil {
					for _, wl := range wLogs {
						if wl.PayerEmail != "" || wl.Monto > 0 {
							models.EnrichBoldCallbackLog(ref, wl.PayerEmail, wl.Monto)
							return
						}
					}
				}
				// 2. Fallback: consultar Bold API directamente para obtener el monto
				if linkID != "" {
					if boldCfg, err := models.GetBoldConfig(); err == nil {
						if _, _, monto, err := services.CheckBoldLinkStatus(boldCfg, linkID); err == nil && monto > 0 {
							models.EnrichBoldCallbackLog(ref, "", monto)
						}
					}
				}
			}(paymentLink)
		}
		estado, fechaPago := verificarPago(ref)
		return c.Render("pago_exitoso", fiber.Map{
			"Ref": ref, "Estado": estado, "FechaPago": fechaPago,
		}, "layouts/landing")
	}

	// Registrar el intento de pago en la tabla de callbacks
	if ref != "" || paymentLink != "" {
		ip := c.Get("X-Real-IP")
		if ip == "" {
			ip = c.Get("X-Forwarded-For")
		}
		if ip == "" {
			ip = c.IP()
		}
		entry := models.BoldCallbackLog{
			Referencia:  ref,
			PaymentLink: paymentLink,
			Params:      paramsJSON,
			Estado:      "pendiente",
			IP:          ip,
			UserAgent:   string(c.Request().Header.UserAgent()),
		}
		_ = models.SaveBoldCallbackLog(entry)
	}

	// Verificar inmediatamente el estado del pago en el servidor
	estado, fechaPago := verificarPago(ref)

	return c.Render("pago_exitoso", fiber.Map{
		"Ref":       ref,
		"Estado":    estado,
		"FechaPago": fechaPago,
	}, "layouts/landing")
}

// verificarPago centraliza la lógica de verificación de pago para una referencia dada.
// Devuelve (confirmado, fechaPago). Lo usan tanto PagoExitosoPage como PagoEstadoAPI.
//
// Flujo (en orden):
//  1. DB: si pago_confirmado = true → listo.
//  2. dlocal_payment_log: si hay log con estado PAID/AUTHORIZED → marca y confirma.
//  3. Bold API: consulta directo usando enlace_pago_link_id si existe.
//  4. dLocal API: consulta directo por order_id.
func verificarPago(ref string) (confirmado bool, fechaPago string) {
	var contratoID uint
	if _, err := fmt.Sscanf(ref, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
		return false, ""
	}

	// ─── 1. DB ────────────────────────────────────────────────────────────────
	contrato, err := models.GetContratoParaVerificacion(contratoID)
	if err != nil {
		return false, ""
	}
	if contrato.PagoConfirmado {
		fp := ""
		if contrato.FechaPago != nil {
			fp = contrato.FechaPago.Format("02/01/2006 15:04")
		}
		return true, fp
	}

	// ─── 2. dlocal_payment_log ────────────────────────────────────────────────
	dlocalLogs, err := models.GetDlocalPaymentLogsByRef(ref)
	if err == nil {
		for _, l := range dlocalLogs {
			if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
				log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dlocal_payment_log (id=%d)", contratoID, l.ID)
				if ok, _ := models.MarcarContratoPagado(contratoID); ok {
					go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
				}
				return true, l.CreatedAt.Format("02/01/2006 15:04")
			}
		}
	}

	// ─── 3. Bold API ─────────────────────────────────────────────────────────
	if contrato.EnlacePagoLinkID != "" {
		boldCfg, boldErr := models.GetBoldConfig()
		if boldErr == nil {
			paid, paymentID, monto, boldApiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID)
			if boldApiErr != nil {
				log.Printf("[PAGO-ESTADO] Bold API error para link %s: %v", contrato.EnlacePagoLinkID, boldApiErr)
			} else if paid {
				log.Printf("[PAGO-ESTADO] Contrato %d confirmado via Bold API (payment_id=%s)", contratoID, paymentID)
				if ok, _ := models.MarcarContratoPagado(contratoID); ok {
					go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
				}
				// Enriquecer callback log con monto y email del cliente
				go models.EnrichBoldCallbackLog(ref, contrato.Cliente.Email, monto)
				if paymentID != "" && !models.IsBoldNotificationDuplicate("api-check-"+paymentID) {
					_ = models.SaveBoldWebhookLog(models.BoldWebhookLog{
						NotificationID: "api-check-" + paymentID,
						Tipo:           "API_CHECK",
						PaymentID:      paymentID,
						Referencia:     ref,
						PayerEmail:     contrato.Cliente.Email,
						Monto:          monto,
						Procesado:      true,
					})
				}
				return true, ""
			}
		}
	}

	// ─── 4. dLocal API ───────────────────────────────────────────────────────
	dlocalCfg, dlocalErr := models.GetLastActiveDlocalApi()
	if dlocalErr == nil {
		paid, paymentID, monto, payerEmail, dlocalApiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, ref)
		if dlocalApiErr != nil {
			log.Printf("[PAGO-ESTADO] dLocal API error para order_id %s: %v", ref, dlocalApiErr)
		} else if paid {
			log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dLocal API (payment_id=%s)", contratoID, paymentID)
			if ok, _ := models.MarcarContratoPagado(contratoID); ok {
				go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
			}
			notifID := "api-check-" + ref
			if !models.IsDlocalNotificationDuplicate(notifID) {
				_ = models.SaveDlocalPaymentLog(models.DlocalPaymentLog{
					NotificationID: notifID,
					Fuente:         "api_check",
					Tipo:           "API_CHECK",
					Estado:         "PAID",
					PaymentID:      paymentID,
					Referencia:     ref,
					PayerEmail:     payerEmail,
					Monto:          monto,
					Procesado:      true,
				})
			}
			return true, ""
		}
	}

	return false, ""
}

// PagoEstadoAPI devuelve el estado de pago de un contrato para polling desde el frontend.
// GET /api/pago-estado?ref=contrato-{id}
func PagoEstadoAPI(c *fiber.Ctx) error {
	ref := c.Query("ref", "")
	if ref == "" {
		return c.JSON(fiber.Map{"confirmado": false, "error": "ref requerido"})
	}
	confirmado, fechaPago := verificarPago(ref)
	if confirmado {
		return c.JSON(fiber.Map{"confirmado": true, "fecha_pago": fechaPago})
	}
	return c.JSON(fiber.Map{"confirmado": false})
}
