package controllers

import (
	"fmt"
	"time"

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

// ─── Configuración ────────────────────────────────────────────────────────────

// HostingerConfigPage renderiza la vista de gestión de Hostinger.
func HostingerConfigPage(c *fiber.Ctx) error {
	cfg, _ := models.GetHostingerConfig()
	if err := c.Render("hostinger", fiber.Map{
		"Title":   "Hostinger API",
		"Config":  cfg,
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}, "layouts/main"); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return nil
}

// SaveHostingerConfig guarda o actualiza el token de Hostinger.
func SaveHostingerConfig(c *fiber.Ctx) error {
	type body struct {
		ID    uint   `json:"id" form:"id"`
		Token string `json:"token" form:"token"`
		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.Token == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "token requerido"})
	}

	cfg := models.HostingerConfig{
		Nota:  b.Nota,
		Token: b.Token,
	}
	cfg.ID = b.ID

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

// ─── Helpers ─────────────────────────────────────────────────────────────────

func hostingerClient() (*services.HostingerClient, error) {
	cfg, err := models.GetHostingerConfig()
	if err != nil {
		return nil, err
	}
	return services.NewHostingerClient(cfg.Token), nil
}

// ─── Endpoints de datos ───────────────────────────────────────────────────────

// GetHostingerVPS devuelve la lista de VPS/VMs y actualiza automáticamente
// los servidores vinculados (IP, RAM, CPU, disco, estado, vencimiento).
func GetHostingerVPS(c *fiber.Ctx) error {
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{
			"error": "No se encontró configuración activa de Hostinger",
		})
	}
	data, err := client.GetVPSList()
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	// Auto-sync: actualizar servidores vinculados por hostinger_vps_id
	go syncServidoresFromVPSList(client, data)
	return c.JSON(fiber.Map{"data": data})
}

// GetHostingerDomains devuelve el portafolio de dominios.
func GetHostingerDomains(c *fiber.Ctx) error {
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{
			"error": "No se encontró configuración activa de Hostinger",
		})
	}
	data, err := client.GetDomains()
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"data": data})
}

// GetHostingerDNS devuelve los registros DNS y los nameservers de un dominio (:domain).
func GetHostingerDNS(c *fiber.Ctx) error {
	domain := c.Params("domain")
	if domain == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"})
	}
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{
			"error": "No se encontró configuración activa de Hostinger",
		})
	}
	records, err := client.GetDNSRecords(domain)
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	// Nameservers son opcionales: si falla (ej. VPS hostnames no están en portafolio) se ignora.
	ns, _ := client.GetDomainNameServers(domain)
	return c.JSON(fiber.Map{"data": records, "name_servers": ns, "domain": domain})
}

// GetHostingerOrders devuelve las órdenes de facturación y actualiza
// automáticamente el vencimiento de los servidores vinculados.
func GetHostingerOrders(c *fiber.Ctx) error {
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{
			"error": "No se encontró configuración activa de Hostinger",
		})
	}
	data, err := client.GetOrders()
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	// Auto-sync: actualizar vencimiento de servidores que ya tienen subscription_id vinculado
	go syncVencimientoFromOrders(data)
	return c.JSON(fiber.Map{"data": data})
}

// GetHostingerHosting devuelve las cuentas de hosting.
func GetHostingerHosting(c *fiber.Ctx) error {
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{
			"error": "No se encontró configuración activa de Hostinger",
		})
	}
	data, err := client.GetHostingAccounts()
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"data": data})
}

// ─── DNS: escritura ──────────────────────────────────────────────────────────

// UpdateHostingerDNS agrega o actualiza registros DNS.
func UpdateHostingerDNS(c *fiber.Ctx) error {
	domain := c.Params("domain")
	if domain == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"})
	}
	var body struct {
		Overwrite bool                             `json:"overwrite"`
		Zone      []services.HostingerDNSZoneInput `json:"zone"`
	}
	if err := c.BodyParser(&body); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.UpdateDNSZone(domain, body.Overwrite, body.Zone); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "DNS actualizado"})
}

// ResetHostingerDNS resetea la zona DNS de un dominio.
func ResetHostingerDNS(c *fiber.Ctx) error {
	domain := c.Params("domain")
	if domain == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"})
	}
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.ResetDNS(domain); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Zona DNS reseteada"})
}

// ─── Domains: escritura ──────────────────────────────────────────────────────

// UpdateHostingerNameservers actualiza los nameservers de un dominio.
func UpdateHostingerNameservers(c *fiber.Ctx) error {
	domain := c.Params("domain")
	var body struct {
		NS1 string `json:"ns1"`
		NS2 string `json:"ns2"`
		NS3 string `json:"ns3"`
		NS4 string `json:"ns4"`
	}
	if err := c.BodyParser(&body); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.UpdateNameservers(domain, body.NS1, body.NS2, body.NS3, body.NS4); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Nameservers actualizados"})
}

// EnableHostingerDomainLock activa el bloqueo del dominio.
func EnableHostingerDomainLock(c *fiber.Ctx) error {
	return hostingerDomainToggle(c, "domain-lock", true)
}

// DisableHostingerDomainLock desactiva el bloqueo del dominio.
func DisableHostingerDomainLock(c *fiber.Ctx) error {
	return hostingerDomainToggle(c, "domain-lock", false)
}

// EnableHostingerPrivacy activa la protección WHOIS.
func EnableHostingerPrivacy(c *fiber.Ctx) error {
	return hostingerDomainToggle(c, "privacy", true)
}

// DisableHostingerPrivacy desactiva la protección WHOIS.
func DisableHostingerPrivacy(c *fiber.Ctx) error {
	return hostingerDomainToggle(c, "privacy", false)
}

func hostingerDomainToggle(c *fiber.Ctx, feature string, enable bool) error {
	domain := c.Params("domain")
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	var e error
	switch feature {
	case "domain-lock":
		e = client.SetDomainLock(domain, enable)
	case "privacy":
		e = client.SetPrivacyProtection(domain, enable)
	}
	if e != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": e.Error()})
	}
	return c.JSON(fiber.Map{"message": "Operación realizada"})
}

// ─── VPS: acciones ───────────────────────────────────────────────────────────

// StartHostingerVPS arranca una VM.
func StartHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "start") }

// StopHostingerVPS apaga una VM.
func StopHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "stop") }

// RestartHostingerVPS reinicia una VM.
func RestartHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "restart") }

func vpsAction(c *fiber.Ctx, action string) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	client, errC := hostingerClient()
	if errC != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.VPSAction(id, action); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": action + " enviado"})
}

// SetHostingerVPSRootPassword cambia la contraseña root de una VM.
func SetHostingerVPSRootPassword(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var body struct {
		Password string `json:"password"`
	}
	if err := c.BodyParser(&body); err != nil || body.Password == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password requerido"})
	}
	client, errC := hostingerClient()
	if errC != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.SetVPSRootPassword(id, body.Password); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Contraseña actualizada"})
}

// SetHostingerVPSHostname cambia el hostname de una VM.
func SetHostingerVPSHostname(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var body struct {
		Hostname string `json:"hostname"`
	}
	if err := c.BodyParser(&body); err != nil || body.Hostname == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "hostname requerido"})
	}
	client, errC := hostingerClient()
	if errC != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	if err := client.SetVPSHostname(id, body.Hostname); err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Hostname actualizado"})
}

// GetHostingerVPSMetrics retorna las métricas de una VM.
// Acepta query params date_from y date_to (RFC3339). Por defecto: últimas 24 h.
func GetHostingerVPSMetrics(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	client, errC := hostingerClient()
	if errC != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	now := time.Now().UTC()
	dateFrom := c.Query("date_from", now.Add(-24*time.Hour).Format(time.RFC3339))
	dateTo := c.Query("date_to", now.Format(time.RFC3339))
	data, err := client.GetVPSMetrics(id, dateFrom, dateTo)
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(data)
}

// GetHostingerVPSBackups retorna los backups de una VM.
func GetHostingerVPSBackups(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	client, errC := hostingerClient()
	if errC != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	data, err := client.GetVPSBackups(id)
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(data)
}

// ─── Billing: acciones ───────────────────────────────────────────────────────

// EnableHostingerAutoRenewal activa la autorenovación de una suscripción.
func EnableHostingerAutoRenewal(c *fiber.Ctx) error {
	return hostingerAutoRenewalToggle(c, true)
}

// DisableHostingerAutoRenewal desactiva la autorenovación de una suscripción.
func DisableHostingerAutoRenewal(c *fiber.Ctx) error {
	return hostingerAutoRenewalToggle(c, false)
}

func hostingerAutoRenewalToggle(c *fiber.Ctx, enable bool) error {
	subID := c.Params("id")
	if subID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id requerido"})
	}
	client, err := hostingerClient()
	if err != nil {
		return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
	}
	sub, err := client.ToggleAutoRenewal(subID, enable)
	if err != nil {
		return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"data": sub})
}

// ─── Auto-sync background ────────────────────────────────────────────────────

// syncVencimientoFromOrders actualiza el campo vencimiento de todos los servidores
// que tienen hostinger_subscription_id vinculado, usando los datos de billing ya
// descargados (sin llamadas adicionales a la API).
func syncVencimientoFromOrders(orders []services.HostingerSubscription) {
	db := app.Http.Database.DB

	// Indexar suscripciones por ID para búsqueda O(1)
	subByID := make(map[string]services.HostingerSubscription, len(orders))
	for _, sub := range orders {
		subByID[sub.ID] = sub
	}

	var servidores []models.Servidor
	db.Where("hostinger_subscription_id != '' AND hostinger_subscription_id IS NOT NULL").Find(&servidores)

	for _, srv := range servidores {
		sub, ok := subByID[srv.HostingerSubscriptionID]
		if !ok {
			continue
		}
		venc := sub.ExpiresAt
		if venc == "" {
			venc = sub.NextBillingAt
		}
		if venc == "" || len(venc) < 10 {
			continue
		}
		db.Model(&models.Servidor{}).Where("id = ?", srv.ID).Update("vencimiento", venc[:10])
	}
}

// syncServidoresFromVPSList actualiza specs y vencimiento de todos los servidores
// que tienen hostinger_vps_id vinculado, usando la lista de VPS ya descargada.
// También auto-descubre el vínculo comparando la IP del VPS con ip_servidor.
func syncServidoresFromVPSList(client *services.HostingerClient, vpsList []services.HostingerVPS) {
	db := app.Http.Database.DB

	// Cargar todos los servidores
	var todos []models.Servidor
	db.Find(&todos)

	// Índice por hostinger_vps_id (ya vinculados)
	byVpsID := make(map[int]*models.Servidor, len(todos))
	// Índice por IP (para auto-descubrimiento)
	byIP := make(map[string]*models.Servidor, len(todos))
	for i := range todos {
		if todos[i].HostingerVpsID != nil {
			byVpsID[*todos[i].HostingerVpsID] = &todos[i]
		}
		if todos[i].IpServidor != "" {
			byIP[todos[i].IpServidor] = &todos[i]
		}
	}

	for _, vps := range vpsList {
		// 1. Buscar por hostinger_vps_id ya guardado
		srv := byVpsID[vps.ID]

		// 2. Auto-descubrir por IP si no está vinculado
		if srv == nil {
			for _, ip4 := range vps.IPV4 {
				if s, ok := byIP[ip4.Address]; ok {
					srv = s
					break
				}
			}
		}

		if srv == nil {
			continue
		}

		updates := map[string]interface{}{
			"hostinger_vps_id": vps.ID,
			"hostinger_state":  vps.State,
		}
		if len(vps.IPV4) > 0 && vps.IPV4[0].Address != "" {
			updates["ip_servidor"] = vps.IPV4[0].Address
		}
		if vps.RAMBytes > 0 {
			updates["ram"] = fmt.Sprintf("%.0f GB", float64(vps.RAMBytes)/1024.0)
		}
		if vps.CPU > 0 {
			updates["nucleos"] = fmt.Sprintf("%d", vps.CPU)
		}
		if vps.DiskBytes > 0 {
			updates["disco"] = fmt.Sprintf("%d GB", vps.DiskBytes/1024)
		}

		// Guardar subscription_id del VPS para el puente con facturación
		subID := vps.SubscriptionID
		if subID == "" {
			subID = srv.HostingerSubscriptionID
		}
		if subID != "" {
			updates["hostinger_subscription_id"] = subID
			// Intentar traer fecha de vencimiento desde billing
			if sub, err := client.GetSubscriptionByID(subID); err == nil {
				venc := sub.ExpiresAt
				if venc == "" {
					venc = sub.NextBillingAt
				}
				if venc != "" && len(venc) >= 10 {
					updates["vencimiento"] = venc[:10]
				}
			}
		}

		db.Model(&models.Servidor{}).Where("id = ?", srv.ID).Updates(updates)
	}
}
