package migrations

import (
	"fmt"
	"log"
	"time"

	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// Session structure for managing sessions
type Session struct {
	ID        uint   `gorm:"primaryKey"`
	SessionID string `gorm:"unique;not null"`
	Data      string `gorm:"type:text"`
	CreatedAt time.Time
	UpdatedAt time.Time
	ExpireAt  time.Time
}

// Migrate performs the database migrations
func Migrate() {
	log.Println("Initiating migration...")

	// Migrate the main application models
	if err := app.Http.Database.DB.Migrator().AutoMigrate(
		&models.Roles{},
		&models.Users{},
		&models.Modules{},
		&models.Submodules{},
		&models.Inicio{},
		&models.TipoServidor{},
		&models.TipoDb{},
		&models.Servidor{},
		&models.ConxDb{},
		&models.ConxSsh{},
		&models.ProvServidor{},
		&models.QrVcard{},
		&models.OssApi{},
		&models.VcfVcard{},
		&models.DlocalApi{},
		// Módulo de Renovaciones
		&models.Servicio{},
		&models.Cliente{},
		&models.PlantillaCorreo{},
		&models.NotificacionRegla{},
		&models.Contrato{},
		&models.NotificacionLog{},
		&models.SmtpConfig{},
		// Integraciones externas
		&models.HostingerConfig{},
		&models.CloudflareConfig{},
		// Query Runner
		&models.QueryHistory{},
		// Pasarelas de pago
		&models.BoldConfig{},
		&models.BoldWebhookLog{},
		&models.BoldCallbackLog{},
		&models.DlocalPaymentLog{},
		&models.PaypalConfig{},
		// Documentación por SaaS
		&models.SaasProducto{},
		&models.DocCategoria{},
		&models.DocPagina{},
		// Integraciones SaaS (dispatcher de pagos)
		&models.SaasApiConfig{},
		&models.SaasDispatchLog{},
		&models.SaasWebhookInLog{},
		// Documentos de clientes
		&models.ClienteDocumento{},
		// Telegram
		&models.TelegramConfig{},
		&models.TelegramLog{},
		// Portal de Clientes
		&models.PortalUser{},
		&models.PortalAcceso{},
		&models.Proyecto{},
		&models.ProyectoFase{},
		&models.ProyectoAvance{},
		&models.ProyectoEntregable{},
		&models.ProyectoDocumento{},
		&models.ProyectoTicket{},
		&models.TicketMensaje{},
		&models.Factura{},
		// Sistema de notificaciones por evento
		&models.NotifEventoConfig{},
		&models.SistemaNotificacion{},
		&models.ServidorAlertaUmbral{},
		// Submódulo Partner
		&models.PartnerRecurso{},
		&models.PartnerComunicado{},
		// Integración VCard API Admin (Laravel)
		&models.VcardApiConfig{},
		// Integración Coolify
		&models.CoolifyConfig{},
		// Historial de métricas del agente
		&models.ServidorMetricasHistory{},
		// Monitor de disponibilidad de URLs
		&models.UrlMonitor{},
		&models.UrlMonitorLog{},
		// Tablero de tareas
		&models.Tarea{},
		&models.TareaComentario{},
		// Automatización de cotizaciones, contratos, actas y cuentas de cobro con IA
		&models.PlantillaDocumento{},
		&models.Tarifa{},
		&models.DocumentoGenerado{},
		&models.Arquitectura{},
	); err != nil {
		log.Fatalf("Error during main migration: %v", err)
	}

	// Open (or create) a SQLite database for sessions
	db, err := gorm.Open(sqlite.Open("session.db"), &gorm.Config{})
	if err != nil {
		log.Fatalf("Error connecting to the database: %v", err)
		return
	}

	// Migrate the Session structure
	if err := db.AutoMigrate(&Session{}); err != nil {
		log.Fatalf("Error migrating session database: %v", err)
		return
	}

	fmt.Println("Session database created and table migrated successfully.")

	// Insertar módulo y submódulos de Renovaciones
	SeedRenovaciones()

	// Insertar módulo y submódulos de Integraciones externas
	SeedIntegraciones()

	// Insertar submódulo "Pasarelas de Pago" en el módulo Integraciones
	SeedPasarelas()

	// Insertar submódulos de SaaS (Productos + Integraciones) en el módulo Integraciones
	SeedSaas()

	// Insertar submódulo "Statuspage" en el módulo Integraciones
	SeedStatuspage()

	// Crear plantillas y reglas mínimas si no existen (bienvenida, pago_recibido)
	SeedPlantillasBase()

	// Agregar submódulo "Telegram" al módulo Integraciones
	SeedTelegram()

	// Crear módulo y submódulos de Portal de Clientes
	SeedPortalClientes()

	// Agregar submódulo "Partner Recursos" al módulo Portal de Clientes
	SeedPartnerRecursos()

	// Crear módulo y submódulos de Automatización IA (cotizaciones, contratos, asistente)
	SeedAutomatizacionIA()

	log.Println("Migration Completed...")
}

// SeedStatuspage agrega el submódulo "Statuspage" (Atlassian) al módulo "Integraciones"
// y lo asigna a todos los roles. Es idempotente.
func SeedStatuspage() {
	db := app.Http.Database.DB

	// Obtener el módulo "Integraciones" (debe existir luego de SeedIntegraciones)
	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		log.Printf("[SEED] Módulo 'Integraciones' no encontrado para SeedStatuspage: %v", err)
		return
	}

	entries := []struct{ title, desc, url string }{
		{"Statuspage", "Monitoreo de estado de servicios vía Atlassian Statuspage", "/app/statuspage"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
		}
		insertados = append(insertados, sub)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulo Statuspage al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulo Statuspage asignado al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de Statuspage completado.")
}

// SeedPlantillasBase crea una plantilla mínima y la regla de pago_recibido si no existen.
// Es idempotente — no sobreescribe registros existentes.
func SeedPlantillasBase() {
	db := app.Http.Database.DB

	plantillasBase := []struct {
		nombre string
		asunto string
		tipo   string
		cuerpo string
	}{
		{
			nombre: "Pago recibido (base)",
			asunto: "Pago recibido — gracias por tu pago",
			tipo:   "pago",
			cuerpo: `<!DOCTYPE html><html><body style="font-family:sans-serif;color:#333;max-width:600px;margin:auto;padding:24px">
<h2 style="color:#5a7d2b">Pago recibido ✔</h2>
<p>Hola <strong>{{.ClienteNombre}}</strong>,</p>
<p>Hemos recibido y confirmado tu pago. Tu servicio ha sido renovado correctamente.</p>
<table style="width:100%;border-collapse:collapse;margin:16px 0">
{{range .Servicios}}
<tr style="border-bottom:1px solid #eee">
  <td style="padding:8px">{{.Nombre}}</td>
  <td style="padding:8px;text-align:right">{{.Moneda}} {{.Precio}}</td>
  <td style="padding:8px;text-align:right;color:#888">Vence: {{.FechaVenc}}</td>
</tr>
{{end}}
</table>
<p style="font-size:13px;color:#888">Si tienes alguna pregunta, responde a este correo.</p>
</body></html>`,
		},
		{
			nombre: "Bienvenida (base)",
			asunto: "Bienvenido — tu servicio está activo",
			tipo:   "bienvenida",
			cuerpo: `<!DOCTYPE html><html><body style="font-family:sans-serif;color:#333;max-width:600px;margin:auto;padding:24px">
<h2 style="color:#5a7d2b">¡Bienvenido!</h2>
<p>Hola <strong>{{.ClienteNombre}}</strong>,</p>
<p>Tu servicio ha sido activado. A continuación el detalle:</p>
<table style="width:100%;border-collapse:collapse;margin:16px 0">
{{range .Servicios}}
<tr style="border-bottom:1px solid #eee">
  <td style="padding:8px">{{.Nombre}}</td>
  <td style="padding:8px;text-align:right">{{.Moneda}} {{.Precio}}</td>
  <td style="padding:8px;text-align:right;color:#888">Vence: {{.FechaVenc}}</td>
</tr>
{{end}}
</table>
<p style="font-size:13px;color:#888">Gracias por elegirnos.</p>
</body></html>`,
		},
	}

	for _, p := range plantillasBase {
		var existing models.PlantillaCorreo
		if err := db.Where("nombre = ?", p.nombre).First(&existing).Error; err != nil {
			nueva := models.PlantillaCorreo{
				Nombre:     p.nombre,
				Asunto:     p.asunto,
				Tipo:       p.tipo,
				CuerpoHTML: p.cuerpo,
			}
			if err := db.Create(&nueva).Error; err != nil {
				log.Printf("[SEED] Error creando plantilla '%s': %v", p.nombre, err)
				continue
			}
			log.Printf("[SEED] Plantilla '%s' creada (ID %d)", p.nombre, nueva.ID)
		} else {
			log.Printf("[SEED] Plantilla '%s' ya existe (ID %d)", existing.Nombre, existing.ID)
		}
	}

	// Crear regla pago_recibido si no existe
	var reglaExistente models.NotificacionRegla
	if err := db.Where("tipo_evento = ?", "pago_recibido").First(&reglaExistente).Error; err != nil {
		// Buscar la plantilla de pago
		var plantilla models.PlantillaCorreo
		if err2 := db.Where("tipo = ?", "pago").First(&plantilla).Error; err2 != nil {
			log.Printf("[SEED] No se encontró plantilla de tipo 'pago' para crear regla pago_recibido")
			return
		}
		regla := models.NotificacionRegla{
			Nombre:         "Confirmación de pago",
			TipoEvento:     "pago_recibido",
			PlantillaID:    plantilla.ID,
			Activo:         true,
			AplicaA:        "todos",
			PasarelaEnlace: "ninguna",
		}
		if err := db.Create(&regla).Error; err != nil {
			log.Printf("[SEED] Error creando regla pago_recibido: %v", err)
		} else {
			log.Printf("[SEED] Regla 'pago_recibido' creada (ID %d) con plantilla ID %d", regla.ID, plantilla.ID)
		}
	} else {
		log.Printf("[SEED] Regla 'pago_recibido' ya existe (ID %d)", reglaExistente.ID)
	}

	log.Println("[SEED] SeedPlantillasBase completado.")
}

// MigrateRenovaciones crea/actualiza las tablas del módulo de Renovaciones.
// Es idempotente: GORM AutoMigrate solo añade columnas/tablas nuevas, nunca las borra.
func MigrateRenovaciones() {
	db := app.Http.Database.DB
	if err := db.AutoMigrate(
		&models.Servicio{},
		&models.Cliente{},
		&models.PlantillaCorreo{},
		&models.NotificacionRegla{},
		&models.Contrato{}, // incluye columnas enlace_pago y enlace_pago_link_id
		&models.NotificacionLog{},
		&models.SmtpConfig{},
	); err != nil {
		log.Printf("[MIGRATE] Error en MigrateRenovaciones: %v", err)
	} else {
		log.Println("[MIGRATE] Tablas de Renovaciones OK")
	}
}

// SeedRenovaciones inserta el módulo "Renovaciones" y sus submódulos si no existen,
// y los asigna a todos los roles presentes en la BD.
func SeedRenovaciones() {
	db := app.Http.Database.DB

	// 1. Crear/obtener módulo
	var modulo models.Modules
	result := db.Where("title = ?", "Renovaciones").First(&modulo)
	if result.Error != nil {
		modulo = models.Modules{
			Title:       "Renovaciones",
			Description: "Gestión de contratos, clientes y notificaciones de vencimiento",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Renovaciones: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Renovaciones' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Renovaciones' ya existe (ID %d)", modulo.ID)
	}

	// 2. Definir submódulos
	entries := []struct{ title, desc, url string }{
		{"Contratos", "Gestión de contratos y vencimientos", "/app/contratos"},
		{"Clientes", "Clientes y datos de contacto", "/app/clientes"},
		{"Servicios", "Catálogo de servicios", "/app/servicios"},
		{"Plantillas Correo", "Editor de plantillas de correo", "/app/plantillas-correo"},
		{"Reglas de Aviso", "Reglas de notificación automática", "/app/reglas-notificacion"},
		{"Historial Notif.", "Historial de correos enviados", "/app/historial-notificaciones"},
		{"Config. SMTP", "Configuración del servidor de correo", "/app/smtp-config"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			// Asegurar que el submódulo apunte al módulo correcto (Renovaciones)
			if sub.ModuleId != modulo.ID {
				if err := db.Model(&sub).Update("module_id", modulo.ID).Error; err != nil {
					log.Printf("[SEED] Error actualizando module_id de '%s': %v", sub.Title, err)
				} else {
					log.Printf("[SEED] Submódulo '%s' reasignado a módulo Renovaciones", sub.Title)
				}
			} else {
				log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
			}
		}
		insertados = append(insertados, sub)
	}

	// 3. Asignar solo al rol Administrador
	var rolAdmin models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rolAdmin).Association("Submodules").Append(&insertados)
		log.Printf("[SEED] Submódulos de Renovaciones asignados al rol 'Administrador'")
	}

	log.Println("[SEED] Seed de Renovaciones completado.")
}

// SeedAutomatizacionIA inserta el módulo "Automatización IA" y sus submódulos
// (Asistente, Plantillas de Documento, Tarifas) si no existen, y los asigna al
// rol Administrador. Es idempotente.
func SeedAutomatizacionIA() {
	db := app.Http.Database.DB

	// 1. Crear/obtener módulo
	var modulo models.Modules
	result := db.Where("title = ?", "Automatización IA").First(&modulo)
	if result.Error != nil {
		modulo = models.Modules{
			Title:       "Automatización IA",
			Description: "Cotizaciones, contratos, actas y cuentas de cobro generados por IA",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Automatización IA: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Automatización IA' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Automatización IA' ya existe (ID %d)", modulo.ID)
	}

	// 2. Definir submódulos
	entries := []struct{ title, desc, url string }{
		{"Asistente", "Chat propio: mismo motor y tools que el bot de Telegram", "/app/asistente"},
		{"Plantillas de Documento", "Fuente de verdad de cotización, contrato, acta y cuenta de cobro", "/app/automatizacion/plantillas"},
		{"Tarifas", "Valor por hora, licencias, VMs y márgenes usados al cotizar", "/app/automatizacion/tarifas"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			if sub.ModuleId != modulo.ID {
				if err := db.Model(&sub).Update("module_id", modulo.ID).Error; err != nil {
					log.Printf("[SEED] Error actualizando module_id de '%s': %v", sub.Title, err)
				} else {
					log.Printf("[SEED] Submódulo '%s' reasignado a módulo Automatización IA", sub.Title)
				}
			} else {
				log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
			}
		}
		insertados = append(insertados, sub)
	}

	// 3. Asignar solo al rol Administrador
	var rolAdmin models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rolAdmin).Association("Submodules").Append(&insertados)
		log.Printf("[SEED] Submódulos de Automatización IA asignados al rol 'Administrador'")
	}

	log.Println("[SEED] Seed de Automatización IA completado.")
}

// SeedIntegraciones crea el módulo "Integraciones" con los submódulos
// de Hostinger y Cloudflare, y los asigna a todos los roles existentes.
// Es idempotente: si el registro ya existe (por URL) lo reutiliza sin duplicar.
func SeedIntegraciones() {
	db := app.Http.Database.DB

	// 1. Crear/obtener módulo
	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		modulo = models.Modules{
			Title:       "Integraciones",
			Description: "Conexión con servicios externos: Hostinger, Cloudflare y más",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Integraciones: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Integraciones' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Integraciones' ya existe (ID %d)", modulo.ID)
	}

	// 2. Definir submódulos
	entries := []struct{ title, desc, url string }{
		{"Hostinger", "Panel de VPS, dominios, hosting y DNS de Hostinger", "/app/hostinger"},
		{"Cloudflare", "Gestión de zonas, DNS, SSL y firewall en Cloudflare", "/app/cloudflare"},
		{"VCard API", "Integración con el sistema VCard externo (Laravel + Sanctum): usuarios, membresías, vcards, pagos y más", "/app/vcard-api"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			if sub.ModuleId != modulo.ID {
				if err := db.Model(&sub).Update("module_id", modulo.ID).Error; err != nil {
					log.Printf("[SEED] Error actualizando module_id de '%s': %v", sub.Title, err)
				} else {
					log.Printf("[SEED] Submódulo '%s' reasignado a módulo Integraciones", sub.Title)
				}
			} else {
				log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
			}
		}
		insertados = append(insertados, sub)
	}

	// 3. Asignar a todos los roles existentes
	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulos al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulos de Integraciones asignados al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de Integraciones completado.")
}

// SeedPasarelas agrega el submódulo "Pasarelas de Pago" al módulo "Integraciones"
// y lo asigna a todos los roles. Es idempotente.
func SeedPasarelas() {
	db := app.Http.Database.DB

	// Obtener el módulo "Integraciones" (debe existir luego de SeedIntegraciones)
	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		log.Printf("[SEED] Módulo 'Integraciones' no encontrado para SeedPasarelas: %v", err)
		return
	}

	entries := []struct{ title, desc, url string }{
		{"Pasarelas de Pago", "Configuración de Bold, dLocal y otras pasarelas", "/app/pasarelas-pago"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
		}
		insertados = append(insertados, sub)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulos al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulo Pasarelas asignado al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de Pasarelas completado.")
}

// SeedServidores crea el módulo "Servidores" con los submódulos de gestión
// de servidores, conexiones SSH/DB, tipos y el Query Runner. Es idempotente.
func SeedServidores() {
	db := app.Http.Database.DB

	// 1. Crear/obtener módulo
	var modulo models.Modules
	if err := db.Where("title = ?", "Servidores").First(&modulo).Error; err != nil {
		modulo = models.Modules{
			Title:       "Servidores",
			Description: "Gestión de servidores, conexiones de base de datos y Query Runner",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Servidores: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Servidores' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Servidores' ya existe (ID %d)", modulo.ID)
	}

	// 2. Definir submódulos
	entries := []struct{ title, desc, url string }{
		{"Servidores", "Lista y gestión de servidores registrados", "/app/servidor"},
		{"Conexión DB", "Conexiones de base de datos a servidores", "/app/conexion_db"},
		{"Conexión SSH", "Conexiones SSH a servidores", "/app/conexion_ssh"},
		{"Tipo DB", "Catálogo de tipos de base de datos", "/app/tipo_db"},
		{"Tipo Servidor", "Catálogo de tipos de servidor", "/app/tipo_servidor"},
		{"Proveedor Servidor", "Catálogo de proveedores de servidor", "/app/prov_servidor"},
		{"Query Runner", "Editor SQL con historial y exportación", "/app/query-runner"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			if sub.ModuleId != modulo.ID {
				db.Model(&sub).Update("module_id", modulo.ID)
				log.Printf("[SEED] Submódulo '%s' reasignado a módulo Servidores", sub.Title)
			} else {
				log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
			}
		}
		insertados = append(insertados, sub)
	}

	// 3. Asignar a todos los roles existentes
	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulos al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulos de Servidores asignados al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de Servidores completado.")
}

// SeedAdministracion crea el módulo "Administración" con sus submódulos
// (Roles, Módulos, Submódulos, Usuarios) y los asigna a todos los roles. Es idempotente.
func SeedAdministracion() {
	db := app.Http.Database.DB

	var modulo models.Modules
	if err := db.Where("title = ?", "Administración").First(&modulo).Error; err != nil {
		modulo = models.Modules{
			Title:       "Administración",
			Description: "Gestión de usuarios, roles, módulos y permisos del sistema",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Administración: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Administración' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Administración' ya existe (ID %d)", modulo.ID)
	}

	entries := []struct{ title, desc, url string }{
		{"Roles", "Gestión de roles y permisos", "/app/roles"},
		{"Módulos", "Gestión de módulos del menú lateral", "/app/modules"},
		{"Submódulos", "Gestión de submódulos y URLs", "/app/submodules"},
		{"Usuarios", "Gestión de usuarios del sistema", "/app/users"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			if sub.ModuleId != modulo.ID {
				db.Model(&sub).Update("module_id", modulo.ID)
				log.Printf("[SEED] Submódulo '%s' reasignado a módulo Administración", sub.Title)
			} else {
				log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
			}
		}
		insertados = append(insertados, sub)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulos al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulos de Administración asignados al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de Administración completado.")
}

// SeedSaas agrega los submódulos de gestión de productos SaaS e integraciones
// de pago al módulo "Integraciones" ya existente. Es idempotente.
func SeedSaas() {
	db := app.Http.Database.DB

	// Reutilizar el módulo "Integraciones" (creado por SeedIntegraciones)
	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		log.Printf("[SEED] Módulo 'Integraciones' no encontrado para SeedSaas: %v", err)
		return
	}

	entries := []struct{ title, desc, url string }{
		{"Productos SaaS", "Gestión de productos SaaS y su documentación asociada", "/app/saas"},
		{"Integraciones SaaS", "Callbacks de pago hacia APIs externas y logs de despacho", "/app/saas-api"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
		}
		insertados = append(insertados, sub)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		log.Printf("[SEED] Error obteniendo roles: %v", err)
		return
	}
	for _, rol := range roles {
		if err := db.Model(&rol).Association("Submodules").Append(&insertados); err != nil {
			log.Printf("[SEED] Error asignando submódulos SaaS al rol '%s': %v", rol.Name, err)
		} else {
			log.Printf("[SEED] Submódulos SaaS asignados al rol '%s'", rol.Name)
		}
	}

	log.Println("[SEED] Seed de SaaS completado.")
}

// MigratePortal ejecuta AutoMigrate para los modelos del portal de clientes.
func MigratePortal() {
	db := app.Http.Database.DB
	if err := db.AutoMigrate(
		&models.Proyecto{},
		&models.ProyectoFase{},
		&models.ProyectoAvance{},
		&models.ProyectoEntregable{},
		&models.ProyectoDocumento{},
		&models.ProyectoTicket{},
		&models.TicketMensaje{},
		&models.PortalUser{},
		&models.PortalAcceso{},
		&models.TelegramPortalToken{},
		&models.Factura{},
	); err != nil {
		log.Printf("[MIGRATE] Error en MigratePortal: %v", err)
	} else {
		log.Println("[MIGRATE] MigratePortal completado.")
	}
}

// MigrateLanding crea/actualiza la tabla de sesiones del Landing Generator.
func MigrateLanding() {
	db := app.Http.Database.DB
	if err := db.AutoMigrate(&models.LandingSession{}, &models.AiConfig{}); err != nil {
		log.Printf("[MIGRATE] Error en MigrateLanding: %v", err)
	} else {
		log.Println("[MIGRATE] Tablas landing_sessions y ai_configs OK")
	}
}

// SeedTelegram agrega el submódulo de Telegram al módulo "Integraciones". Es idempotente.
func SeedTelegram() {
	db := app.Http.Database.DB

	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		log.Printf("[SEED] Módulo 'Integraciones' no encontrado para SeedTelegram: %v", err)
		return
	}

	url := "/app/telegram"
	var sub models.Submodules
	if err := db.Where("url = ?", url).First(&sub).Error; err != nil {
		sub = models.Submodules{
			Title:       "Telegram",
			Description: "Configuración de bots de Telegram para notificaciones",
			Url:         url,
			ModuleId:    modulo.ID,
			ModifiedAt:  time.Now(),
		}
		if err2 := db.Create(&sub).Error; err2 != nil {
			log.Printf("[SEED] Error creando submódulo Telegram: %v", err2)
			return
		}
		log.Printf("[SEED] Submódulo 'Telegram' creado (ID %d)", sub.ID)
	} else {
		log.Printf("[SEED] Submódulo 'Telegram' ya existe (ID %d)", sub.ID)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		return
	}
	for _, rol := range roles {
		db.Model(&rol).Association("Submodules").Append(&sub)
	}
	log.Println("[SEED] Seed de Telegram completado.")
}

// SeedPortalClientes agrega el módulo "Portal de Clientes" con sus submódulos. Es idempotente.
func SeedPortalClientes() {
	db := app.Http.Database.DB

	var modulo models.Modules
	if err := db.Where("title = ?", "Portal de Clientes").First(&modulo).Error; err != nil {
		modulo = models.Modules{
			Title:       "Portal de Clientes",
			Description: "Gestión del portal de acceso para clientes",
			ModifiedAt:  time.Now(),
		}
		if err2 := db.Create(&modulo).Error; err2 != nil {
			log.Printf("[SEED] Error creando módulo 'Portal de Clientes': %v", err2)
			return
		}
		log.Printf("[SEED] Módulo 'Portal de Clientes' creado (ID %d)", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Portal de Clientes' ya existe (ID %d)", modulo.ID)
	}

	entries := []struct{ title, desc, url string }{
		{"Proyectos", "Gestión de proyectos y roadmap para clientes", "/app/proyectos"},
		{"Facturas", "Gestión de facturas y cobros del portal", "/app/facturas"},
		{"Portal Usuarios", "Gestión de usuarios del portal de clientes", "/app/portal-usuarios"},
	}

	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title:       e.title,
				Description: e.desc,
				Url:         e.url,
				ModuleId:    modulo.ID,
				ModifiedAt:  time.Now(),
			}
			if err2 := db.Create(&sub).Error; err2 != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err2)
				continue
			}
			log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
		} else {
			log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
		}
		insertados = append(insertados, sub)
	}

	var roles []models.Roles
	if err := db.Find(&roles).Error; err != nil {
		return
	}
	for _, rol := range roles {
		db.Model(&rol).Association("Submodules").Append(&insertados)
	}
	log.Println("[SEED] Seed de Portal de Clientes completado.")
}

// SeedNotifDefaults inserta configuraciones de notificación por defecto (idempotente).
// Solo crea registros que no existen; no sobreescribe cambios del admin.
func SeedNotifDefaults() {
	db := app.Http.Database.DB
	defaults := []models.NotifEventoConfig{
		// Tickets
		{Evento: "ticket_nuevo", Destinatario: "admin", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Admin recibe cuando el cliente abre un ticket"},
		{Evento: "ticket_respuesta_cliente", Destinatario: "admin", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Admin recibe cuando el cliente responde un ticket"},
		{Evento: "ticket_respuesta_admin", Destinatario: "portal_user", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Portal user recibe cuando el admin responde"},
		{Evento: "factura_subida", Destinatario: "portal_user", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Portal user recibe cuando se sube una factura"},
		// Servidores
		{Evento: "servidor_caido", Destinatario: "admin", CanalEmail: false, CanalTelegram: true, CanalSistema: true, Descripcion: "Admin recibe cuando un servidor deja de reportar"},
		{Evento: "servidor_vence_pronto", Destinatario: "admin", CanalEmail: true, CanalTelegram: true, CanalSistema: true, Descripcion: "Admin recibe cuando un VPS está próximo a vencer"},
		{Evento: "servidor_recurso_alto", Destinatario: "admin", CanalEmail: false, CanalTelegram: true, CanalSistema: true, Descripcion: "Admin recibe cuando CPU/RAM/Disco supera el umbral"},
		// Tareas
		{Evento: "tarea_asignada", Destinatario: "admin", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando se asigna una tarea a un usuario"},
		{Evento: "tarea_estado", Destinatario: "admin", CanalEmail: false, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando cambia el estado de una tarea"},
		{Evento: "tarea_comentario", Destinatario: "admin", CanalEmail: false, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando hay un nuevo comentario en una tarea"},
	}
	created := 0
	for _, d := range defaults {
		var existing models.NotifEventoConfig
		if err := db.Where("evento = ? AND destinatario = ?", d.Evento, d.Destinatario).First(&existing).Error; err != nil {
			db.Create(&d)
			created++
		}
	}
	log.Printf("[SEED] SeedNotifDefaults: %d nuevas configuraciones creadas.", created)
}

// SeedShield es un stub idempotente para la configuración de Shield.
func SeedShield() {
	log.Println("[SEED] SeedShield: sin entradas por defecto definidas aún.")
}

// SeedPartnerRecursos agrega el submódulo "Partner Recursos" al módulo "Portal de Clientes". Es idempotente.
func SeedPartnerRecursos() {
	db := app.Http.Database.DB

	var modulo models.Modules
	if err := db.Where("title = ?", "Portal de Clientes").First(&modulo).Error; err != nil {
		log.Printf("[SEED] Módulo 'Portal de Clientes' no encontrado para SeedPartnerRecursos: %v", err)
		return
	}

	url := "/app/partner-recursos"
	var sub models.Submodules
	if err := db.Where("url = ?", url).First(&sub).Error; err != nil {
		sub = models.Submodules{
			Title:       "Partner Recursos",
			Description: "Documentación y comunicados exclusivos para partners",
			Url:         url,
			ModuleId:    modulo.ID,
			ModifiedAt:  time.Now(),
		}
		if err2 := db.Create(&sub).Error; err2 != nil {
			log.Printf("[SEED] Error creando submódulo Partner Recursos: %v", err2)
			return
		}
		log.Printf("[SEED] Submódulo 'Partner Recursos' creado (ID %d)", sub.ID)
	} else {
		log.Printf("[SEED] Submódulo 'Partner Recursos' ya existe (ID %d)", sub.ID)
	}

	var rolAdmin models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rolAdmin).Association("Submodules").Append(&sub)
		log.Printf("[SEED] Submódulo 'Partner Recursos' asignado al rol 'Administrador'")
	}
	log.Println("[SEED] Seed de Partner Recursos completado.")
}

// SeedContabilidadMenu crea el módulo "Contabilidad" y sus submódulos en el menú lateral.
func SeedContabilidadMenu() {
	db := app.Http.Database.DB
	var modulo models.Modules
	result := db.Where("title = ?", "Contabilidad").First(&modulo)
	if result.Error != nil {
		modulo = models.Modules{
			Title:       "Contabilidad",
			Description: "Gestión contable: ingresos, egresos, cuentas por cobrar/pagar",
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&modulo).Error; err != nil {
			log.Printf("[SEED] Error creando módulo Contabilidad: %v", err)
			return
		}
		log.Printf("[SEED] Módulo 'Contabilidad' creado con ID %d", modulo.ID)
	} else {
		log.Printf("[SEED] Módulo 'Contabilidad' ya existe (ID %d)", modulo.ID)
	}
	entries := []struct{ title, desc, url string }{
		{"Dashboard", "Resumen mensual", "/app/contabilidad"},
		{"Transacciones", "Ingresos y egresos", "/app/contabilidad/transacciones"},
		{"Categorías", "Plan de cuentas", "/app/contabilidad/cuentas"},
		{"Entidades", "Clientes y proveedores", "/app/contabilidad/entidades"},
		{"Cuentas por cobrar", "Pendientes de cobro", "/app/contabilidad/cuentas-cobro"},
		{"Cuentas por pagar", "Pendientes de pago", "/app/contabilidad/cuentas-pagar"},
	}
	var insertados []models.Submodules
	for _, e := range entries {
		var sub models.Submodules
		if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
			sub = models.Submodules{
				Title: e.title, Description: e.desc, Url: e.url,
				ModuleId: modulo.ID, ModifiedAt: time.Now(),
			}
			if err := db.Create(&sub).Error; err != nil {
				log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
				continue
			}
		} else if sub.ModuleId != modulo.ID {
			db.Model(&sub).Update("module_id", modulo.ID)
		}
		insertados = append(insertados, sub)
	}
	var rolAdmin models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rolAdmin).Association("Submodules").Append(&insertados)
		log.Printf("[SEED] Submódulos de Contabilidad asignados al rol 'Administrador'")
	}
	log.Println("[SEED] Seed de Contabilidad completado.")
}

// SeedWebSms crea el submódulo de WebSMS bajo "Integraciones".
func SeedWebSms() {
	db := app.Http.Database.DB
	var modulo models.Modules
	if err := db.Where("title = ?", "Integraciones").First(&modulo).Error; err != nil {
		log.Println("[SEED] Módulo 'Integraciones' no encontrado, se creará")
		modulo = models.Modules{
			Title: "Integraciones", Description: "Conexión con servicios externos",
			ModifiedAt: time.Now(),
		}
		db.Create(&modulo)
	}
	url := "/app/websms"
	var sub models.Submodules
	if err := db.Where("url = ?", url).First(&sub).Error; err != nil {
		sub = models.Submodules{
			Title: "WebSMS", Description: "Notificaciones SMS vía LabsMobile",
			Url: url, ModuleId: modulo.ID, ModifiedAt: time.Now(),
		}
		if err := db.Create(&sub).Error; err != nil {
			log.Printf("[SEED] Error creando submódulo WebSMS: %v", err)
			return
		}
		log.Printf("[SEED] Submódulo 'WebSMS' creado")
	} else if sub.ModuleId != modulo.ID {
		db.Model(&sub).Update("module_id", modulo.ID)
	}
	var rolAdmin models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rolAdmin).Association("Submodules").Append(&[]models.Submodules{sub})
		log.Printf("[SEED] Submódulo 'WebSMS' asignado al rol 'Administrador'")
	}
	log.Println("[SEED] Seed de WebSMS completado.")
}

// SeedUrlMonitor agrega el submódulo "Monitor de URLs" al módulo "Servidores". Es idempotente.
func SeedUrlMonitor() {
	db := app.Http.Database.DB
	var modulo models.Modules
	if err := db.Where("title = ?", "Servidores").First(&modulo).Error; err != nil {
		log.Println("[SEED] Módulo 'Servidores' no encontrado, se omite SeedUrlMonitor")
		return
	}
	url := "/app/url-monitor"
	var sub models.Submodules
	if err := db.Where("url = ?", url).First(&sub).Error; err != nil {
		sub = models.Submodules{
			Title:       "Monitor de URLs",
			Description: "Monitoreo de disponibilidad y latencia de sitios y APIs",
			Url:         url,
			ModuleId:    modulo.ID,
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&sub).Error; err != nil {
			log.Printf("[SEED] Error creando submódulo 'Monitor de URLs': %v", err)
			return
		}
		log.Printf("[SEED] Submódulo 'Monitor de URLs' creado")
	} else if sub.ModuleId != modulo.ID {
		db.Model(&sub).Update("module_id", modulo.ID)
	}
	var rol models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rol).Association("Submodules").Append(&[]models.Submodules{sub})
		log.Printf("[SEED] Submódulo 'Monitor de URLs' asignado al rol 'Administrador'")
	}
	log.Println("[SEED] Seed de Monitor de URLs completado.")
}

// SeedTareas agrega el submódulo "Tareas" al módulo "Administración" solo para el rol Administrador. Es idempotente.
func SeedTareas() {
	db := app.Http.Database.DB
	var modulo models.Modules
	if err := db.Where("title = ?", "Administración").First(&modulo).Error; err != nil {
		log.Println("[SEED] Módulo 'Administración' no encontrado, se omite SeedTareas")
		return
	}
	url := "/app/tareas"
	var sub models.Submodules
	if err := db.Where("url = ?", url).First(&sub).Error; err != nil {
		sub = models.Submodules{
			Title:       "Tareas",
			Description: "Tablero kanban de tareas internas",
			Url:         url,
			ModuleId:    modulo.ID,
			ModifiedAt:  time.Now(),
		}
		if err := db.Create(&sub).Error; err != nil {
			log.Printf("[SEED] Error creando submódulo 'Tareas': %v", err)
			return
		}
		log.Printf("[SEED] Submódulo 'Tareas' creado")
	} else if sub.ModuleId != modulo.ID {
		db.Model(&sub).Update("module_id", modulo.ID)
	}
	var rol models.Roles
	if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
		log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
	} else {
		db.Model(&rol).Association("Submodules").Append(&[]models.Submodules{sub})
		log.Printf("[SEED] Submódulo 'Tareas' asignado al rol 'Administrador'")
	}
	log.Println("[SEED] Seed de Tareas completado.")
}
