package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"log"
	"crypto/tls"
	"math/big"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/cynfo/rmm-backend/api"
	"github.com/cynfo/rmm-backend/config"
	"github.com/cynfo/rmm-backend/db"
	"github.com/cynfo/rmm-backend/monitor"
	"github.com/cynfo/rmm-backend/ws"
)

func main() {
	cfgPath := "config.yaml"
	if len(os.Args) > 1 {
		cfgPath = os.Args[1]
	}

	cfg, err := config.Load(cfgPath)
	if err != nil {
		log.Fatalf("Konfiguration laden fehlgeschlagen: %v", err)
	}

	// TLS-Zertifikate pruefen/generieren
	if err := ensureTLS(cfg.TLSCert, cfg.TLSKey); err != nil {
		log.Fatalf("TLS-Setup fehlgeschlagen: %v", err)
	}

	// Datenbank initialisieren
	database, err := db.New(cfg.Database)
	if err != nil {
		log.Fatalf("Datenbank-Fehler: %v", err)
	}
	defer database.Close()

	// WebSocket Hub starten
	hub := ws.NewHub()
	hub.SetEventCallback(func(agentID, eventType, message string) {
		database.InsertAgentEvent(agentID, eventType, message)
	})
	go hub.Run()

	// Inaktivitaets-Monitor starten
	mon := monitor.New(database)
	go mon.Run()

	// Task-Scheduler starten
	scheduler := api.NewTaskScheduler(database, hub)
	go scheduler.Run()

	// Config API-Keys in DB migrieren (einmalig)
	database.MigrateConfigAPIKeys(cfg.APIKeys)

	// API-Key Cache (laedt Keys aus DB, refresht alle 30s)
	apiKeyCache := api.NewAPIKeyCache(database)

	// Auth-Handler initialisieren
	authHandler := api.NewAuthHandler(database, cfg.JWTSecret)
	authHandler.EnsureDefaultAdmin()

	// Router aufbauen - Auth-Routes ohne Auth-Middleware
	authMux := http.NewServeMux()
	authHandler.SetupAuthRoutes(authMux)

	// Geschuetzte Routes
	protectedMux := http.NewServeMux()
	handler := api.NewHandler(database)
	handler.SetScheduler(scheduler)
	handler.SetupRoutes(protectedMux, hub)
	// GET /api/v1/auth/me braucht JWT
	protectedMux.HandleFunc("GET /api/v1/auth/me", authHandler.Me)

	// Geschuetzte 2FA-Routen
	protectedMux.HandleFunc("POST /api/v1/auth/2fa/setup", authHandler.TOTPSetup)
	protectedMux.HandleFunc("POST /api/v1/auth/2fa/confirm", authHandler.TOTPConfirm)
	protectedMux.HandleFunc("POST /api/v1/auth/2fa/disable", authHandler.TOTPDisable)

	// Combined router: auth routes ungeschuetzt, rest mit DB-basierter Auth
	mux := http.NewServeMux()
	mux.Handle("POST /api/v1/auth/login", authMux)
	mux.Handle("POST /api/v1/auth/2fa/validate", authMux) // 2FA-Schritt 2 ebenfalls ohne JWT

	// Terminal: JWT-only (kein API-Key reicht — erfordert echten Login mit 2FA)
	terminalMux := http.NewServeMux()
	terminalMux.HandleFunc("GET /api/v1/agents/{id}/terminal", handler.TerminalHandler(hub))
	mux.Handle("GET /api/v1/agents/{id}/terminal", api.JWTOnlyAuth(authHandler, terminalMux))

	mux.Handle("/", api.CombinedAuthWithCache(apiKeyCache, authHandler, protectedMux))

	// Middleware-Chain: CORS -> Logging -> Handler
	var chain http.Handler = mux
	chain = api.Logging(chain)
	chain = api.CORS(chain)

	log.Printf("RMM Backend startet auf %s (TLS)", cfg.ListenAddr)
	log.Printf("API-Keys (Config): %d, DB-basierte Validierung aktiv", len(cfg.APIKeys))

	// HTTP/2 deaktivieren: WebSocket-Upgrades benoetigen HTTP/1.1
	srv := &http.Server{
		Addr:      cfg.ListenAddr,
		Handler:   chain,
		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
	}
	if err := srv.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey); err != nil {
		log.Fatalf("Server-Fehler: %v", err)
	}
}

// ensureTLS generiert self-signed Zertifikate wenn keine vorhanden
func ensureTLS(certPath, keyPath string) error {
	if _, err := os.Stat(certPath); err == nil {
		if _, err := os.Stat(keyPath); err == nil {
			log.Println("TLS-Zertifikate gefunden")
			return nil
		}
	}

	log.Println("Generiere self-signed TLS-Zertifikat...")

	// Verzeichnis erstellen
	if dir := filepath.Dir(certPath); dir != "" && dir != "." {
		os.MkdirAll(dir, 0755)
	}
	if dir := filepath.Dir(keyPath); dir != "" && dir != "." {
		os.MkdirAll(dir, 0755)
	}

	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return fmt.Errorf("Key generieren: %w", err)
	}

	serial, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))

	template := x509.Certificate{
		SerialNumber: serial,
		Subject: pkix.Name{
			Organization: []string{"RMM Backend"},
			CommonName:   "rmm-backend",
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(365 * 24 * time.Hour * 10), // 10 Jahre
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IPAddresses:           []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("192.168.85.13")},
		DNSNames:              []string{"localhost", "rmm-backend"},
	}

	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
	if err != nil {
		return fmt.Errorf("Zertifikat erstellen: %w", err)
	}

	certFile, err := os.Create(certPath)
	if err != nil {
		return err
	}
	defer certFile.Close()
	pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certDER})

	keyBytes, err := x509.MarshalECPrivateKey(key)
	if err != nil {
		return err
	}

	keyFile, err := os.OpenFile(keyPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer keyFile.Close()
	pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})

	log.Printf("TLS-Zertifikat generiert: %s, %s", certPath, keyPath)
	return nil
}
