package controllers

import (
	"fmt"

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

// PasarelasPage renderiza la vista unificada de pasarelas de pago.
func PasarelasPage(c *fiber.Ctx) error {
	boldCfg, _ := models.GetBoldConfig()
	dlocalCfg, _ := models.GetLastActiveDlocalApi()
	paypalCfg, _ := models.GetPaypalConfig()

	data := fiber.Map{
		"Title":   "Pasarelas de Pago",
		"Bold":    boldCfg,
		"Dlocal":  dlocalCfg,
		"Paypal":  paypalCfg,
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}
	if err := c.Render("pasarelas_pago", data, "layouts/main"); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return nil
}

// ─── Bold ─────────────────────────────────────────────────────────────────────

// SaveBoldConfig guarda o actualiza la configuración de Bold.
func SaveBoldConfig(c *fiber.Ctx) error {
	type body struct {
		ID            uint   `json:"id"             form:"id"`
		ApiKeyProd    string `json:"api_key_prod"    form:"api_key_prod"`
		SecretKeyProd string `json:"secret_key_prod" form:"secret_key_prod"`
		ApiKeyTest    string `json:"api_key_test"    form:"api_key_test"`
		SecretKeyTest string `json:"secret_key_test" form:"secret_key_test"`
		Modo          string `json:"modo"            form:"modo"`
		CallbackUrl   string `json:"callback_url"    form:"callback_url"`
		Nota          string `json:"nota"            form:"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.Modo == "" {
		b.Modo = "test"
	}

	cfg := models.BoldConfig{
		ApiKeyProd:    b.ApiKeyProd,
		SecretKeyProd: b.SecretKeyProd,
		ApiKeyTest:    b.ApiKeyTest,
		SecretKeyTest: b.SecretKeyTest,
		Modo:          b.Modo,
		CallbackUrl:   b.CallbackUrl,
		Nota:          b.Nota,
	}
	cfg.ID = b.ID

	if err := models.SaveBoldConfig(cfg); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Configuración Bold guardada"})
}

// GetBoldConfig devuelve la configuración activa de Bold (para cargar el form).
func GetBoldConfigAPI(c *fiber.Ctx) error {
	cfg, err := models.GetBoldConfig()
	if err != nil {
		return c.JSON(fiber.Map{"data": nil})
	}
	return c.JSON(fiber.Map{"data": cfg})
}

// ─── dLocal ───────────────────────────────────────────────────────────────────

// SaveDlocalConfig guarda o actualiza la configuración de dLocal.
func SaveDlocalConfigWeb(c *fiber.Ctx) error {
	type body struct {
		ID                 uint   `json:"id"                    form:"id"`
		AccessKeyID        string `json:"access_key_id"         form:"access_key_id"`
		AccessKeySecret    string `json:"access_key_secret"     form:"access_key_secret"`
		AccessKeyIDdev     string `json:"access_key_id_dev"     form:"access_key_id_dev"`
		AccessKeySecretdev string `json:"access_key_secret_dev" form:"access_key_secret_dev"`
		UrlProd            string `json:"url_prod"              form:"url_prod"`
		UrlDev             string `json:"url_dev"               form:"url_dev"`
		Modo               string `json:"modo"                  form:"modo"`
	}
	var b body
	if err := c.BodyParser(&b); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if b.Modo == "" {
		b.Modo = "dev"
	}

	cfg := models.DlocalApi{
		AccessKeyID:        b.AccessKeyID,
		AccessKeySecret:    b.AccessKeySecret,
		AccessKeyIDdev:     b.AccessKeyIDdev,
		AccessKeySecretdev: b.AccessKeySecretdev,
		UrlProd:            b.UrlProd,
		UrlDev:             b.UrlDev,
		Modo:               b.Modo,
		IsActive:           true,
	}
	cfg.ID = b.ID

	// Desactivar configs previas
	if cfg.ID == 0 {
		if err := models.CreateDlocalApi(&cfg); err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
	} else {
		if err := models.UpdateDlocalApi(&cfg); err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
	}
	return c.JSON(fiber.Map{"message": "Configuración dLocal guardada"})
}

// GetDlocalConfigAPI devuelve la configuración activa de dLocal.
func GetDlocalConfigAPI(c *fiber.Ctx) error {
	cfg, err := models.GetLastActiveDlocalApi()
	if err != nil {
		return c.JSON(fiber.Map{"data": nil})
	}
	return c.JSON(fiber.Map{"data": cfg})
}

// ─── Logs Bold ────────────────────────────────────────────────────────────────

// BoldWebhookLogs devuelve los logs de notificaciones con paginación y filtro por tipo.
// Query params: page (default 1), limit (default 25), tipo (SALE_APPROVED|SALE_REJECTED|...|TODOS)
func BoldWebhookLogs(c *fiber.Ctx) error {
	page := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 25)
	tipo := c.Query("tipo", "")
	if page < 1 {
		page = 1
	}
	logs, total, err := models.GetBoldWebhookLogsPaginated(page, limit, tipo)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"data":  logs,
		"total": total,
		"page":  page,
		"limit": limit,
	})
}

// BoldCallbackLogs devuelve los intentos de pago (visitas al callback) con paginación y filtro.
// Query params: page (default 1), limit (default 25), estado (pendiente|pagado|fallido|revertido|TODOS)
func BoldCallbackLogs(c *fiber.Ctx) error {
	page := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 25)
	estado := c.Query("estado", "")
	if page < 1 {
		page = 1
	}
	logs, total, err := models.GetBoldCallbackLogsPaginated(page, limit, estado)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"data":  logs,
		"total": total,
		"page":  page,
		"limit": limit,
	})
}

// ─── Logs dLocal ──────────────────────────────────────────────────────────────

// DlocalPaymentLogs devuelve los últimos 100 registros de pagos de dLocal (legacy).
func DlocalPaymentLogs(c *fiber.Ctx) error {
	logs, err := models.GetDlocalPaymentLogs(100)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"data": logs})
}

// DlocalPaymentLogsPaginated devuelve los logs con paginación y filtro por estado.
// Query params: page (default 1), limit (default 25), estado (PAID|PENDING|REJECTED|APROBADOS|TODOS)
func DlocalPaymentLogsPaginated(c *fiber.Ctx) error {
	page := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 25)
	estado := c.Query("estado", "")
	if page < 1 {
		page = 1
	}
	logs, total, err := models.GetDlocalPaymentLogsPaginated(page, limit, estado)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"data":  logs,
		"total": total,
		"page":  page,
		"limit": limit,
	})
}

// ValidarDlocalLog verifica el estado de pago real de un log dLocal consultando
// todas las fuentes disponibles (dlocal_payment_log → Bold API → dLocal API).
// Si se confirma el pago, marca el contrato como pagado y envía confirmación.
// POST /app/pasarelas/dlocal/logs/:id/validar
func ValidarDlocalLog(c *fiber.Ctx) error {
	logID, err := c.ParamsInt("id")
	if err != nil || logID <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
	}

	var entry models.DlocalPaymentLog
	if err := models.GetDlocalPaymentLogByID(uint(logID), &entry); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Log no encontrado"})
	}

	ref := entry.Referencia
	if ref == "" {
		ref = entry.OrderID
	}

	var contratoID uint
	if _, err := fmt.Sscanf(ref, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Referencia no tiene formato contrato-{id}"})
	}

	contrato, err := models.GetContratoParaVerificacion(contratoID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Contrato no encontrado"})
	}

	payerEmail := entry.PayerEmail
	if payerEmail == "" {
		payerEmail = contrato.Cliente.Email
	}
	monto := entry.Monto
	pagoConfirmado := contrato.PagoConfirmado
	fuenteConfirmacion := ""

	// ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
	if pagoConfirmado {
		fuenteConfirmacion = "db"
	}

	// ─── 2. Mismo log si ya tiene estado PAID ────────────────────────────────
	if !pagoConfirmado && (entry.Estado == "PAID" || entry.Estado == "AUTHORIZED") {
		pagoConfirmado = true
		fuenteConfirmacion = "dlocal_log"
	}

	// ─── 3. Bold API ─────────────────────────────────────────────────────────
	if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
		boldCfg, boldErr := models.GetBoldConfig()
		if boldErr == nil {
			paid, _, apiMonto, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID)
			if apiErr == nil && paid {
				pagoConfirmado = true
				fuenteConfirmacion = "bold_api"
				if apiMonto > 0 {
					monto = float64(apiMonto)
				}
			}
		}
	}

	// ─── 4. dLocal API ───────────────────────────────────────────────────────
	if !pagoConfirmado {
		dlocalCfg, dlErr := models.GetLastActiveDlocalApi()
		if dlErr == nil {
			paid, _, apiMonto, apiEmail, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, ref)
			if apiErr == nil && paid {
				pagoConfirmado = true
				fuenteConfirmacion = "dlocal_api"
				if apiMonto > 0 {
					monto = apiMonto
				}
				if apiEmail != "" {
					payerEmail = apiEmail
				}
			}
		}
	}

	// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
	if pagoConfirmado && !contrato.PagoConfirmado {
		if ok, _ := models.MarcarContratoPagado(contratoID); ok {
			go services.EnviarCorreoConfirmacionPago(contratoID, fuenteConfirmacion)
		}
	}

	_ = models.UpdateDlocalPaymentLogDatos(uint(logID), payerEmail, monto)
	_ = models.GetDlocalPaymentLogByID(uint(logID), &entry)

	msg := "Datos actualizados"
	if pagoConfirmado && !contrato.PagoConfirmado {
		msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuenteConfirmacion)
	} else if pagoConfirmado {
		msg = fmt.Sprintf("Pago ya confirmado (%s)", fuenteConfirmacion)
	} else {
		msg = "Pago aún no confirmado — datos actualizados"
	}

	return c.JSON(fiber.Map{
		"ok":         true,
		"confirmado": pagoConfirmado,
		"fuente":     fuenteConfirmacion,
		"mensaje":    msg,
		"data":       entry,
	})
}

// ─── Validación de intento de pago (callback) ───────────────────────────────

// ValidarBoldCallback verifica si el pago de un intento pendiente realmente se realizó.
// Recorre las mismas fuentes que verificarPago() y, si confirma, marca el contrato + actualiza el callback.
// POST /app/pasarelas/bold/callbacks/:id/validar
func ValidarBoldCallback(c *fiber.Ctx) error {
	cbID, err := c.ParamsInt("id")
	if err != nil || cbID <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
	}

	var cb models.BoldCallbackLog
	if err := models.GetBoldCallbackLogByID(uint(cbID), &cb); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Intento no encontrado"})
	}

	var contratoID uint
	if _, err := fmt.Sscanf(cb.Referencia, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Referencia no tiene formato contrato-{id}"})
	}

	contrato, err := models.GetContratoParaVerificacion(contratoID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Contrato no encontrado"})
	}

	pagoConfirmado := contrato.PagoConfirmado
	fuente := ""

	// ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
	if pagoConfirmado {
		fuente = "db"
	}

	// ─── 2. dlocal_payment_log ───────────────────────────────────────────────
	if !pagoConfirmado {
		if dlLogs, dlErr := models.GetDlocalPaymentLogsByRef(cb.Referencia); dlErr == nil {
			for _, l := range dlLogs {
				if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
					pagoConfirmado = true
					fuente = "dlocal_log"
					break
				}
			}
		}
	}

	// ─── 3. Bold API ─────────────────────────────────────────────────────────
	if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
		if boldCfg, boldErr := models.GetBoldConfig(); boldErr == nil {
			if paid, _, _, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID); apiErr == nil && paid {
				pagoConfirmado = true
				fuente = "bold_api"
			}
		}
	}

	// ─── 4. dLocal API ───────────────────────────────────────────────────────
	if !pagoConfirmado {
		if dlocalCfg, dlErr := models.GetLastActiveDlocalApi(); dlErr == nil {
			if paid, _, _, _, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, cb.Referencia); apiErr == nil && paid {
				pagoConfirmado = true
				fuente = "dlocal_api"
			}
		}
	}

	// Actualizar contrato si se confirmó ahora
	if pagoConfirmado && !contrato.PagoConfirmado {
		if ok, _ := models.MarcarContratoPagado(contratoID); ok {
			go services.EnviarCorreoConfirmacionPago(contratoID, fuente)
		}
	}

	// Actualizar estado del callback
	nuevoEstado := cb.Estado
	if pagoConfirmado && cb.Estado == "pendiente" {
		nuevoEstado = "pagado"
		_ = models.UpdateBoldCallbackEstadoByID(uint(cbID), "pagado")
		cb.Estado = "pagado"
	}

	msg := "Pago aún no confirmado"
	if pagoConfirmado && !contrato.PagoConfirmado {
		msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuente)
	} else if pagoConfirmado {
		msg = fmt.Sprintf("Pago ya confirmado (%s)", fuente)
	}
	_ = nuevoEstado

	return c.JSON(fiber.Map{
		"ok":         true,
		"confirmado": pagoConfirmado,
		"fuente":     fuente,
		"mensaje":    msg,
		"data":       cb,
	})
}

// ─── Validación de log API_CHECK ─────────────────────────────────────────────

// ValidarBoldLog verifica el estado de pago real de un log API_CHECK consultando
// todas las fuentes disponibles (dlocal_payment_log → Bold API → dLocal API).
// Si se confirma el pago, marca el contrato como pagado y envía el email de confirmación.
// También rellena email y monto faltantes. Funciona para cualquier tipo de log Bold.
// POST /app/pasarelas/bold/logs/:id/validar
func ValidarBoldLog(c *fiber.Ctx) error {
	logID, err := c.ParamsInt("id")
	if err != nil || logID <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
	}

	// Cargar el registro existente
	var entry models.BoldWebhookLog
	if err := models.GetBoldWebhookLogByID(uint(logID), &entry); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Log no encontrado"})
	}

	// Parsear contrato ID de la referencia (contrato-{id})
	var contratoID uint
	if _, err := fmt.Sscanf(entry.Referencia, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Referencia no tiene formato contrato-{id}"})
	}

	// Cargar contrato con cliente
	contrato, err := models.GetContratoParaVerificacion(contratoID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Contrato no encontrado"})
	}

	// Valores iniciales: reutilizar lo que ya hay en el log, fallback al cliente
	payerEmail := entry.PayerEmail
	if payerEmail == "" {
		payerEmail = contrato.Cliente.Email
	}
	monto := entry.Monto
	pagoConfirmado := contrato.PagoConfirmado
	fuenteConfirmacion := ""

	// ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
	if pagoConfirmado {
		fuenteConfirmacion = "db"
	}

	// ─── 2. dlocal_payment_log ────────────────────────────────────────────────
	if !pagoConfirmado {
		dlocalLogs, dlErr := models.GetDlocalPaymentLogsByRef(entry.Referencia)
		if dlErr == nil {
			for _, l := range dlocalLogs {
				if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
					pagoConfirmado = true
					fuenteConfirmacion = "dlocal_log"
					if l.PayerEmail != "" {
						payerEmail = l.PayerEmail
					}
					if l.Monto > 0 {
						monto = int64(l.Monto)
					}
					break
				}
			}
		}
	}

	// ─── 3. Bold API ─────────────────────────────────────────────────────────
	if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
		boldCfg, boldErr := models.GetBoldConfig()
		if boldErr == nil {
			paid, _, apiMonto, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID)
			if apiErr == nil && paid {
				pagoConfirmado = true
				fuenteConfirmacion = "bold_api"
				if apiMonto > 0 {
					monto = apiMonto
				}
			}
		}
	}

	// ─── 4. dLocal API ───────────────────────────────────────────────────────
	if !pagoConfirmado {
		dlocalCfg, dlErr := models.GetLastActiveDlocalApi()
		if dlErr == nil {
			paid, _, apiMonto, apiEmail, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, entry.Referencia)
			if apiErr == nil && paid {
				pagoConfirmado = true
				fuenteConfirmacion = "dlocal_api"
				if apiMonto > 0 {
					monto = int64(apiMonto)
				}
				if apiEmail != "" {
					payerEmail = apiEmail
				}
			}
		}
	}

	// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
	if pagoConfirmado && !contrato.PagoConfirmado {
		if ok, _ := models.MarcarContratoPagado(contratoID); ok {
			go services.EnviarCorreoConfirmacionPago(contratoID, fuenteConfirmacion)
		}
	}

	// Actualizar log con los mejores datos disponibles
	_ = models.UpdateBoldWebhookLogDatos(uint(logID), payerEmail, monto)

	// Devolver el log actualizado
	_ = models.GetBoldWebhookLogByID(uint(logID), &entry)

	msg := "Datos actualizados"
	if pagoConfirmado && !contrato.PagoConfirmado {
		msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuenteConfirmacion)
	} else if pagoConfirmado {
		msg = fmt.Sprintf("Pago ya confirmado (%s)", fuenteConfirmacion)
	} else {
		msg = "Pago aún no confirmado — datos del contrato actualizados"
	}

	return c.JSON(fiber.Map{
		"ok":         true,
		"confirmado": pagoConfirmado,
		"fuente":     fuenteConfirmacion,
		"mensaje":    msg,
		"data":       entry,
	})
}

// ─── PayPal ───────────────────────────────────────────────────────────────────

// GetPaypalConfigAPI devuelve la configuración activa de PayPal.
func GetPaypalConfigAPI(c *fiber.Ctx) error {
	cfg, err := models.GetPaypalConfig()
	if err != nil {
		return c.JSON(fiber.Map{"data": nil})
	}
	return c.JSON(fiber.Map{"data": cfg})
}

// SavePaypalConfigWeb guarda o actualiza la configuración de PayPal.
func SavePaypalConfigWeb(c *fiber.Ctx) error {
	type body struct {
		ID                  uint   `json:"id"                    form:"id"`
		ClientIDSandbox     string `json:"client_id_sandbox"     form:"client_id_sandbox"`
		ClientSecretSandbox string `json:"client_secret_sandbox" form:"client_secret_sandbox"`
		ClientIDProd        string `json:"client_id_prod"        form:"client_id_prod"`
		ClientSecretProd    string `json:"client_secret_prod"    form:"client_secret_prod"`
		Modo                string `json:"modo"                  form:"modo"`
		WebhookID           string `json:"webhook_id"            form:"webhook_id"`
		ReturnURL           string `json:"return_url"            form:"return_url"`
		CancelURL           string `json:"cancel_url"            form:"cancel_url"`
		Nota                string `json:"nota"                  form:"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.Modo == "" {
		b.Modo = "sandbox"
	}

	cfg := models.PaypalConfig{
		ClientIDSandbox:     b.ClientIDSandbox,
		ClientSecretSandbox: b.ClientSecretSandbox,
		ClientIDProd:        b.ClientIDProd,
		ClientSecretProd:    b.ClientSecretProd,
		Modo:                b.Modo,
		WebhookID:           b.WebhookID,
		ReturnURL:           b.ReturnURL,
		CancelURL:           b.CancelURL,
		Nota:                b.Nota,
	}
	cfg.ID = b.ID

	if err := models.SavePaypalConfig(cfg); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Configuración PayPal guardada"})
}

