// sogoms-admin : Interface web d'administration pour SOGOMS.
// Gestion des apps, schemas, queries, emails, crons et logs.
package main

import (
	"embed"
	"flag"
	"fmt"
	"html/template"
	"io/fs"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"sogoms.com/internal/admin"
	"sogoms.com/internal/config"
	"sogoms.com/internal/infra"
	"sogoms.com/internal/protocol"
	"sogoms.com/internal/version"
)

//go:embed templates/*.html templates/partials/*.html
var templatesFS embed.FS

//go:embed static/*
var staticFS embed.FS

var (
	port         = flag.Int("port", 9000, "HTTP server port")
	configDir    = flag.String("config", "/config", "Configuration directory")
	secretsDir   = flag.String("secrets", "/secrets", "Secrets directory")
	dbSocket     = flag.String("db-socket", "/run/sogoms-db.1.sock", "DB service socket")
	logsSocket   = flag.String("logs-socket", "/run/sogoms-logs.1.sock", "Logs service socket")
	cronSocket   = flag.String("cron-socket", "/run/sogoms-cron.1.sock", "Cron service socket")
	infraDBPath  = flag.String("infra-db", "/data/infra.db", "Infrastructure SQLite database path")
)

func main() {
	flag.Parse()
	log.SetFlags(log.Ltime | log.Lshortfile)

	// Charger la config admin
	adminConfigPath := *secretsDir + "/admin_users.yaml"
	adminCfg, err := admin.LoadAdminConfig(adminConfigPath)
	if err != nil {
		log.Fatalf("load admin config: %v", err)
	}
	log.Printf("[admin] loaded %d admin users", len(adminCfg.Users))

	// Charger le registry des apps
	registry := config.NewRegistry(*configDir)
	if err := registry.Load(); err != nil {
		log.Fatalf("load config: %v", err)
	}
	log.Printf("[admin] loaded apps: %v", registry.Apps())

	// Pools de connexion aux services
	services := &ServicePool{}
	if *dbSocket != "" {
		services.DB = protocol.NewPool(*dbSocket, 2)
	}
	if *logsSocket != "" {
		services.Logs = protocol.NewPool(*logsSocket, 2)
	}
	if *cronSocket != "" {
		services.Cron = protocol.NewPool(*cronSocket, 2)
	}

	// Session store
	sessions := NewSessionStore(&adminCfg.Session)
	go sessions.Cleanup()

	// Rate limiter
	rateLimiter := NewRateLimiter(&adminCfg.RateLimit)

	// Permission checker
	perms := admin.NewPermissionChecker(adminCfg)

	// Audit logger
	audit := admin.NewAuditLogger(services.Logs)

	// Infrastructure DB
	infraDB, err := infra.Open(*infraDBPath)
	if err != nil {
		log.Fatalf("open infra db: %v", err)
	}
	defer infraDB.Close()
	log.Printf("[admin] infra db opened: %s", *infraDBPath)

	// SSH Pool
	sshPool := infra.NewSSHPool(30 * time.Second)
	defer sshPool.CloseAll()

	// Charger les templates (embedded)
	templates, err := loadTemplates()
	if err != nil {
		log.Fatalf("load templates: %v", err)
	}
	log.Printf("[admin] templates loaded (embedded)")

	// Créer le serveur
	server := &AdminServer{
		adminCfg:    adminCfg,
		registry:    registry,
		sessions:    sessions,
		version:     version.Version,
		rateLimiter: rateLimiter,
		perms:       perms,
		audit:       audit,
		services:    services,
		templates:   templates,
		infraDB:     infraDB,
		sshPool:     sshPool,
	}

	// Router
	mux := http.NewServeMux()

	// Fichiers statiques (CSS, JS)
	staticSubFS, _ := fs.Sub(staticFS, "static")
	staticHandler := http.FileServerFS(staticSubFS)
	mux.Handle("GET /admin/static/", http.StripPrefix("/admin/static/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "public, max-age=31536000")
		staticHandler.ServeHTTP(w, r)
	})))

	// Routes publiques
	mux.HandleFunc("GET /admin/login", server.HandleLoginPage)
	mux.HandleFunc("POST /admin/login", server.HandleLogin)

	// Routes 2FA (session requise mais pas forcément complète)
	mux.HandleFunc("GET /admin/2fa/verify", server.HandleTwoFAPage)
	mux.HandleFunc("POST /admin/2fa/verify", server.HandleTwoFAVerify)
	mux.HandleFunc("GET /admin/2fa/setup", TwoFASetupMiddleware(sessions, adminCfg, server.HandleTwoFASetupPage))
	mux.HandleFunc("POST /admin/2fa/setup", TwoFASetupMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleTwoFASetupConfirm)))
	mux.HandleFunc("POST /admin/2fa/disable", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleTwoFADisable)))
	mux.HandleFunc("GET /admin/security", AuthMiddleware(sessions, adminCfg, server.HandleSecurityPage))
	mux.HandleFunc("GET /admin/users", AuthMiddleware(sessions, adminCfg, server.HandleUsersPage))
	mux.HandleFunc("POST /admin/users/reset-2fa", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleReset2FA)))

	// Routes protégées
	mux.HandleFunc("GET /admin/{$}", AuthMiddleware(sessions, adminCfg, server.HandleDashboard))
	mux.HandleFunc("GET /admin/apps", AuthMiddleware(sessions, adminCfg, server.HandleAppsPage))
	mux.HandleFunc("GET /admin/apps/new", AuthMiddleware(sessions, adminCfg, server.HandleAppNewPage))
	mux.HandleFunc("POST /admin/apps/new", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleAppCreate)))
	mux.HandleFunc("GET /admin/apps/{appID}", AuthMiddleware(sessions, adminCfg, server.HandleAppDetailPage))
	mux.HandleFunc("POST /admin/apps/{appID}/scan", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleAppScanDB)))
	mux.HandleFunc("POST /admin/logout", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleLogout)))

	// API htmx (protégées)
	mux.HandleFunc("GET /admin/api/apps", AuthMiddleware(sessions, adminCfg, server.HandleAPIApps))
	mux.HandleFunc("GET /admin/api/services/health", AuthMiddleware(sessions, adminCfg, server.HandleAPIServicesHealth))
	mux.HandleFunc("GET /admin/api/cron/jobs", AuthMiddleware(sessions, adminCfg, server.HandleAPICronJobs))
	mux.HandleFunc("GET /admin/api/infra/status", AuthMiddleware(sessions, adminCfg, server.HandleAPIInfraStatus))

	// Routes Infrastructure (super_admin only)
	mux.HandleFunc("GET /admin/infra", AuthMiddleware(sessions, adminCfg, server.HandleInfraPage))
	mux.HandleFunc("GET /admin/infra/servers/new", AuthMiddleware(sessions, adminCfg, server.HandleServerNewPage))
	mux.HandleFunc("POST /admin/infra/servers/new", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleServerCreate)))
	mux.HandleFunc("GET /admin/infra/servers/{serverID}", AuthMiddleware(sessions, adminCfg, server.HandleServerDetailPage))
	mux.HandleFunc("POST /admin/infra/servers/{serverID}/delete", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleServerDelete)))
	mux.HandleFunc("POST /admin/infra/servers/{serverID}/test-ssh", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleServerTestSSH)))
	mux.HandleFunc("POST /admin/infra/servers/{serverID}/sync-containers", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleServerSyncContainers)))
	mux.HandleFunc("POST /admin/infra/servers/{serverID}/sync-nginx", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleNginxSyncSites)))
	mux.HandleFunc("POST /admin/infra/servers/{serverID}/nginx-reload", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleNginxReload)))
	mux.HandleFunc("POST /admin/infra/containers/{containerID}/action", AuthMiddleware(sessions, adminCfg,
		CSRFMiddleware(sessions, server.HandleContainerAction)))

	// Handler avec logging
	handler := LoggingMiddleware(mux)

	// Démarrer le serveur
	addr := fmt.Sprintf(":%d", *port)
	httpServer := &http.Server{
		Addr:    addr,
		Handler: handler,
	}

	go func() {
		log.Printf("[admin] sogoms-admin started on %s", addr)
		if err := httpServer.ListenAndServe(); err != http.ErrServerClosed {
			log.Fatalf("server error: %v", err)
		}
	}()

	// Attendre signal d'arrêt
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	<-sigCh

	log.Printf("[admin] shutting down...")
	httpServer.Close()
}

// loadTemplates charge les templates depuis embedded.
func loadTemplates() (*template.Template, error) {
	funcMap := template.FuncMap{
		"safe": func(s string) template.HTML {
			return template.HTML(s)
		},
		"safeURL": func(s string) template.URL {
			return template.URL(s)
		},
	}

	tmpl, err := template.New("").Funcs(funcMap).ParseFS(templatesFS, "templates/*.html", "templates/partials/*.html")
	if err != nil {
		return nil, err
	}

	return tmpl, nil
}
