package config

import (
	"log"
	"os"
	"strings"
)

// Valores que estuvieron publicados en el repositorio y deben considerarse
// comprometidos. Si siguen en uso, cualquiera que haya visto el repo puede
// fabricarse una sesión de administrador o descifrar las contraseñas guardadas.
const (
	jwtAppComprometido = "SECRET_APP"
	jwtApiComprometido = "SECRET_API"
	appKeyComprometida = "1894cde6c936a294a478cff0a9227fd276d86df6573b51af5dc59c9064edf426"
)

// VerificarSecretos se ejecuta al arrancar. Los secretos JWT comprometidos
// abortan el arranque (permiten suplantar a cualquier usuario y el único costo
// de rotarlos es volver a iniciar sesión). La clave de cifrado solo advierte,
// porque cambiarla invalida las contraseñas de integraciones ya guardadas y eso
// requiere volver a capturarlas a mano desde el panel.
func (cfg *AppConfig) VerificarSecretos() {
	var criticos []string

	if esSecretoDebil(cfg.Token.AppJwtSecret, jwtAppComprometido) {
		criticos = append(criticos, "APP_JWT_SECRET")
	}
	if esSecretoDebil(cfg.Token.ApiJwtSecret, jwtApiComprometido) {
		criticos = append(criticos, "API_JWT_SECRET")
	}

	if len(criticos) > 0 {
		log.Printf("[SEGURIDAD] Los siguientes secretos siguen con el valor publicado en el repositorio: %s", strings.Join(criticos, ", "))
		log.Printf("[SEGURIDAD] Con ese valor cualquiera puede firmarse una cookie de sesión y entrar como administrador.")
		log.Printf("[SEGURIDAD] Define variables de entorno con valores nuevos y aleatorios antes de arrancar, por ejemplo:")
		for _, nombre := range criticos {
			log.Printf("[SEGURIDAD]   %s=$(openssl rand -hex 32)", nombre)
		}
		log.Fatalf("[SEGURIDAD] Arranque abortado para no exponer el sistema. Al rotarlos, las sesiones abiertas se cierran y hay que volver a iniciar sesión.")
	}

	if cfg.Server.Key == "" || cfg.Server.Key == appKeyComprometida {
		log.Printf("[SEGURIDAD] APP_KEY tiene el valor por defecto publicado en el repositorio.")
		log.Printf("[SEGURIDAD] Con esa clave, cualquiera con acceso a la base de datos puede descifrar las contraseñas SMTP y de integraciones.")
		log.Printf("[SEGURIDAD] Define APP_KEY=$(openssl rand -hex 32) y vuelve a guardar las contraseñas de SMTP e integraciones desde el panel")
		log.Printf("[SEGURIDAD] (al cambiar la clave, las contraseñas cifradas con la anterior dejan de poder descifrarse).")
	}
}

// esSecretoDebil indica si el secreto está vacío o es el valor comprometido.
func esSecretoDebil(valor, comprometido string) bool {
	v := strings.TrimSpace(valor)
	return v == "" || v == comprometido
}

// SecretoDesdeEntorno lee un secreto del entorno con un valor por defecto.
// Se usa para credenciales que no deben vivir en config.yml.
func SecretoDesdeEntorno(nombre, porDefecto string) string {
	if v := strings.TrimSpace(os.Getenv(nombre)); v != "" {
		return v
	}
	return porDefecto
}
