// Command server starts the Control Center Go backend API server.
package main

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/config"
	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/db"
	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/gateway"
	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/handler"
	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/repository"
	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/router"
)

func main() {
	// ── Configuration ──────────────────────────────────────────────────────
	cfg := config.Load()

	// ── Logging ────────────────────────────────────────────────────────────
	logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: parseLogLevel(cfg.LogLevel),
	}))
	slog.SetDefault(logger)

	// ── Database ───────────────────────────────────────────────────────────
	pool, err := db.New(cfg.DatabaseURL)
	if err != nil {
		slog.Error("database connection failed", "error", err)
		os.Exit(1)
	}
	defer pool.Close()

	// ── Repositories (PostgreSQL-backed) ───────────────────────────────────
	agentRepo := repository.NewAgentRepository(pool.Pool)
	sessionRepo := repository.NewSessionRepository(pool.Pool)
	taskRepo := repository.NewTaskRepository(pool.Pool)
	projectRepo := repository.NewProjectRepository(pool.Pool)

	// ── Seed demo agents on first boot ─────────────────────────────────────
	if err := gateway.SeedDemoAgents(context.Background(), agentRepo); err != nil {
		slog.Error("seed demo agents failed", "error", err)
		os.Exit(1)
	}

	// ── SSE Broker ─────────────────────────────────────────────────────────
	broker := handler.NewBroker()

	// ── HTTP handler ───────────────────────────────────────────────────────
	h := handler.NewHandler(agentRepo, sessionRepo, taskRepo, projectRepo)

	// ── Router ─────────────────────────────────────────────────────────────
	r := router.New(&router.Dependencies{
		Handler:    h,
		Pool:       pool,
		CORSOrigin: cfg.CORSOrigin,
		Broker:     broker,
	})

	// ── Gateway clients (WS primary, REST fallback) ───────────────────
	// WS gateway client (primary path)
	wsClient := gateway.NewWSClient(gateway.WSConfig{
		URL:       cfg.WSGatewayURL,
		AuthToken: cfg.WSGatewayToken,
	}, agentRepo, broker, logger)

	// REST gateway client (fallback — only polls if WS fails to connect)
	gwClient := gateway.NewClient(gateway.Config{
		URL:          cfg.GatewayRestURL,
		PollInterval: cfg.GatewayRestPollInterval,
	}, agentRepo, broker)

	// Wire them together: REST defers to WS when WS is connected
	wsClient.SetRESTClient(gwClient)
	gwClient.SetWSClient(wsClient)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Start WS client first (primary)
	go wsClient.Start(ctx)
	// Start REST client (will wait for WS, then stand down or fall back)
	go gwClient.Start(ctx)

	// ── Server ─────────────────────────────────────────────────────────────
	srv := &http.Server{
		Addr:         fmt.Sprintf(":%d", cfg.Port),
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 15 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	// Graceful shutdown
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		slog.Info("server starting", "port", cfg.Port, "env", cfg.Environment)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			slog.Error("server failed", "error", err)
			os.Exit(1)
		}
	}()

	<-quit
	slog.Info("shutting down server...")

	cancel() // stop gateway clients

	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer shutdownCancel()

	if err := srv.Shutdown(shutdownCtx); err != nil {
		slog.Error("server forced to shutdown", "error", err)
		os.Exit(1)
	}

	slog.Info("server exited cleanly")
}

func parseLogLevel(level string) slog.Level {
	switch level {
	case "debug":
		return slog.LevelDebug
	case "warn":
		return slog.LevelWarn
	case "error":
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}