package models

import (
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"gorm.io/gorm"
)

// BoldConfig almacena las credenciales de Bold (pasarela de pagos colombiana).
// Solo un registro puede estar activo a la vez.
type BoldConfig struct {
	gorm.Model
	// Claves de producción
	ApiKeyProd    string `json:"api_key_prod"    gorm:"column:api_key_prod;type:text"`
	SecretKeyProd string `json:"secret_key_prod" gorm:"column:secret_key_prod;type:text"`
	// Claves de prueba / test
	ApiKeyTest string `json:"api_key_test"    gorm:"column:api_key_test;type:text"`
	// En modo test la secret key es cadena vacía según la doc oficial
	SecretKeyTest string `json:"secret_key_test" gorm:"column:secret_key_test;type:text"`
	// Modo activo: "test" | "production"
	Modo string `json:"modo"         gorm:"column:modo;default:'test'"`
	// URL a la que Bold redirige al usuario tras el pago
	CallbackUrl string `json:"callback_url" gorm:"column:callback_url;type:text"`
	// Nota interna
	Nota   string `json:"nota"   gorm:"column:nota;type:text"`
	Activo bool   `json:"activo" gorm:"column:activo;default:true"`
}

func (BoldConfig) TableName() string { return "bold_config" }

// GetBoldConfig retorna la configuración activa.
func GetBoldConfig() (*BoldConfig, error) {
	var item BoldConfig
	if err := app.Http.Database.DB.Where("activo = ?", true).Order("id DESC").First(&item).Error; err != nil {
		return nil, err
	}
	return &item, nil
}

// SaveBoldConfig desactiva la config previa y guarda la nueva (o actualiza si ya tiene ID).
func SaveBoldConfig(s BoldConfig) error {
	app.Http.Database.DB.Model(&BoldConfig{}).
		Where("activo = ?", true).
		Update("activo", false)
	s.Activo = true
	if s.ID > 0 {
		return app.Http.Database.DB.Model(&s).Updates(map[string]interface{}{
			"api_key_prod":    s.ApiKeyProd,
			"secret_key_prod": s.SecretKeyProd,
			"api_key_test":    s.ApiKeyTest,
			"secret_key_test": s.SecretKeyTest,
			"modo":            s.Modo,
			"callback_url":    s.CallbackUrl,
			"nota":            s.Nota,
			"activo":          true,
		}).Error
	}
	return app.Http.Database.DB.Create(&s).Error
}

// ─── Webhook log para idempotencia ───────────────────────────────────────────

// BoldWebhookLog registra cada notificación recibida de Bold.
// La restricción UNIQUE sobre notification_id evita procesar duplicados.
type BoldWebhookLog struct {
	gorm.Model
	NotificationID string `json:"notification_id" gorm:"column:notification_id;uniqueIndex;type:varchar(64);not null"`
	Tipo           string `json:"tipo"            gorm:"column:tipo;type:varchar(30)"`
	PaymentID      string `json:"payment_id"      gorm:"column:payment_id;type:varchar(64)"`
	Referencia     string `json:"referencia"      gorm:"column:referencia;type:varchar(120)"`
	PayerEmail     string `json:"payer_email"     gorm:"column:payer_email;type:varchar(255)"`
	Monto          int64  `json:"monto"           gorm:"column:monto"`
	Procesado      bool   `json:"procesado"       gorm:"column:procesado;default:false"`
	// Body crudo del webhook para auditoría
	Raw string `json:"raw" gorm:"column:raw;type:text"`
}

func (BoldWebhookLog) TableName() string { return "bold_webhook_log" }

// IsBoldNotificationDuplicate intenta insertar el log. Devuelve true si ya existía.
func IsBoldNotificationDuplicate(notificationID string) bool {
	result := app.Http.Database.DB.
		Where("notification_id = ?", notificationID).
		First(&BoldWebhookLog{})
	return result.Error == nil // nil error = registro encontrado = duplicado
}

// SaveBoldWebhookLog guarda el log del webhook.
func SaveBoldWebhookLog(entry BoldWebhookLog) error {
	return app.Http.Database.DB.Create(&entry).Error
}

// MarkBoldWebhookProcessed marca la notificación como procesada.
func MarkBoldWebhookProcessed(notificationID string) {
	app.Http.Database.DB.Model(&BoldWebhookLog{}).
		Where("notification_id = ?", notificationID).
		Update("procesado", true)
}

// GetBoldWebhookLogs devuelve los últimos N registros del log.
func GetBoldWebhookLogs(limit int) ([]BoldWebhookLog, error) {
	var logs []BoldWebhookLog
	if err := app.Http.Database.DB.Order("id DESC").Limit(limit).Find(&logs).Error; err != nil {
		return nil, err
	}
	return logs, nil
}

// GetBoldWebhookLogsByRef devuelve los webhooks recibidos para una referencia dada.
func GetBoldWebhookLogsByRef(referencia string) ([]BoldWebhookLog, error) {
	var logs []BoldWebhookLog
	if err := app.Http.Database.DB.
		Where("referencia = ?", referencia).
		Order("id DESC").
		Find(&logs).Error; err != nil {
		return nil, err
	}
	return logs, nil
}

// GetBoldWebhookLogsPaginated devuelve los logs con paginación y filtro por tipo.
// El valor especial "APROBADOS" incluye tanto SALE_APPROVED como API_CHECK.
func GetBoldWebhookLogsPaginated(page, limit int, tipo string) ([]BoldWebhookLog, int64, error) {
	var logs []BoldWebhookLog
	var total int64
	db := app.Http.Database.DB.Model(&BoldWebhookLog{})
	switch tipo {
	case "", "TODOS":
		// sin filtro
	case "APROBADOS":
		db = db.Where("tipo IN ?", []string{"SALE_APPROVED", "API_CHECK"})
	default:
		db = db.Where("tipo = ?", tipo)
	}
	db.Count(&total)
	offset := (page - 1) * limit
	if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

// UpdateBoldWebhookLogDatos actualiza email y monto de un log existente (para rellenar datos faltantes en API_CHECK).
func UpdateBoldWebhookLogDatos(id uint, payerEmail string, monto int64) error {
	updates := map[string]interface{}{}
	if payerEmail != "" {
		updates["payer_email"] = payerEmail
	}
	if monto > 0 {
		updates["monto"] = monto
	}
	if len(updates) == 0 {
		return nil
	}
	return app.Http.Database.DB.Model(&BoldWebhookLog{}).Where("id = ?", id).Updates(updates).Error
}

// GetBoldWebhookLogByID carga un registro por su PK.
func GetBoldWebhookLogByID(id uint, out *BoldWebhookLog) error {
	return app.Http.Database.DB.First(out, id).Error
}

// ─── Callback log (intentos de pago) ─────────────────────────────────────────

// BoldCallbackLog registra cada visita a la URL de retorno de Bold.
// Esto captura usuarios que iniciaron el proceso de pago (llegaron al checkout)
// pero pueden haber abandonado, fallado o completado el pago.
type BoldCallbackLog struct {
	gorm.Model
	// Referencia principal recibida de Bold (bold-order-id / payment_link / reference)
	Referencia  string `json:"referencia"   gorm:"column:referencia;type:varchar(120);index"`
	PaymentLink string `json:"payment_link" gorm:"column:payment_link;type:varchar(80)"`
	// Todos los parámetros GET recibidos en JSON (para depuración)
	Params string `json:"params" gorm:"column:params;type:text"`
	// Estado: pendiente | pagado | fallido | revertido
	Estado string `json:"estado" gorm:"column:estado;type:varchar(20);default:'pendiente'"`
	// Datos del pago (enriquecidos desde el webhook cuando llega)
	PayerEmail string `json:"payer_email" gorm:"column:payer_email;type:varchar(255)"`
	Monto      int64  `json:"monto"       gorm:"column:monto;default:0"`
	// Datos de red (para análisis)
	IP        string `json:"ip"         gorm:"column:ip;type:varchar(45)"`
	UserAgent string `json:"user_agent" gorm:"column:user_agent;type:text"`
}

func (BoldCallbackLog) TableName() string { return "bold_callback_log" }

// SaveBoldCallbackLog guarda un registro de intento de pago.
func SaveBoldCallbackLog(entry BoldCallbackLog) error {
	return app.Http.Database.DB.Create(&entry).Error
}

// UpdateBoldCallbackEstado actualiza el estado de los intentos que coincidan con la referencia.
// Se llama desde el webhook cuando llega un evento SALE_APPROVED, SALE_REJECTED, etc.
func UpdateBoldCallbackEstado(referencia, estado string) {
	if referencia == "" {
		return
	}
	app.Http.Database.DB.Model(&BoldCallbackLog{}).
		Where("referencia = ? AND estado = 'pendiente'", referencia).
		Update("estado", estado)
}

// EnrichBoldCallbackLog enriquece los intentos de pago con email y monto del webhook.
// Se llama cuando llega SALE_APPROVED que sí tiene esos datos.
func EnrichBoldCallbackLog(referencia, payerEmail string, monto int64) {
	if referencia == "" {
		return
	}
	updates := map[string]interface{}{}
	if payerEmail != "" {
		updates["payer_email"] = payerEmail
	}
	if monto > 0 {
		updates["monto"] = monto
	}
	if len(updates) == 0 {
		return
	}
	app.Http.Database.DB.Model(&BoldCallbackLog{}).
		Where("referencia = ?", referencia).
		Updates(updates)
}

// GetBoldCallbackLogByID carga un intento de pago por su PK.
func GetBoldCallbackLogByID(id uint, out *BoldCallbackLog) error {
	return app.Http.Database.DB.First(out, id).Error
}

// UpdateBoldCallbackEstadoByID actualiza el estado de un intento de pago específico por su PK.
func UpdateBoldCallbackEstadoByID(id uint, estado string) error {
	return app.Http.Database.DB.Model(&BoldCallbackLog{}).Where("id = ?", id).Update("estado", estado).Error
}

// GetBoldCallbackLogsPaginated devuelve los intentos de pago con paginación y filtro.
func GetBoldCallbackLogsPaginated(page, limit int, estado string) ([]BoldCallbackLog, int64, error) {
	var logs []BoldCallbackLog
	var total int64
	db := app.Http.Database.DB.Model(&BoldCallbackLog{})
	if estado != "" && estado != "TODOS" {
		db = db.Where("estado = ?", estado)
	}
	db.Count(&total)
	offset := (page - 1) * limit
	if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}
