package cmd

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/cors"
	"github.com/spf13/cobra"

	"github.com/insertr/insertr/internal/api"
	"github.com/insertr/insertr/internal/auth"
	"github.com/insertr/insertr/internal/content"
	"github.com/insertr/insertr/internal/db"
	"github.com/insertr/insertr/internal/engine"
)

var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Start the content API server",
	Long: `Start the HTTP API server that provides content storage and retrieval.
Supports both development and production modes with SQLite or PostgreSQL databases.`,
	Run: runServe,
}

var (
	port    int
	devMode bool
)

func init() {
	serveCmd.Flags().IntVarP(&port, "port", "p", 0, "Server port")
	serveCmd.Flags().BoolVar(&devMode, "dev-mode", false, "Enable development mode features")
}

func runServe(cmd *cobra.Command, args []string) {
	// Load configuration
	cfg, err := loadConfig()
	if err != nil {
		log.Fatalf("Failed to load configuration: %v", err)
	}

	// Override with flags if provided
	if port != 0 {
		cfg.Server.Port = port
	}
	if devMode {
		cfg.Auth.DevMode = true
	}

	// Initialize database
	database, err := db.NewDatabase(cfg.Database.Path)
	if err != nil {
		log.Fatalf("Failed to initialize database: %v", err)
	}
	defer database.Close()

	// Support environment variables for sensitive values
	if clientSecret := os.Getenv("AUTHENTIK_CLIENT_SECRET"); clientSecret != "" && cfg.Auth.OIDC != nil {
		cfg.Auth.OIDC.ClientSecret = clientSecret
	}
	if endpoint := os.Getenv("AUTHENTIK_ENDPOINT"); endpoint != "" && cfg.Auth.OIDC != nil {
		cfg.Auth.OIDC.Endpoint = endpoint
	}

	// Set redirect URL if not configured
	if cfg.Auth.OIDC != nil && cfg.Auth.OIDC.RedirectURL == "" {
		cfg.Auth.OIDC.RedirectURL = fmt.Sprintf("http://%s:%d/auth/callback", cfg.Server.Host, cfg.Server.Port)
	}

	// Create legacy auth config for compatibility
	authConfig := &auth.AuthConfig{
		DevMode:   cfg.Auth.DevMode,
		Provider:  cfg.Auth.Provider,
		JWTSecret: cfg.Auth.JWTSecret,
	}

	if cfg.Auth.OIDC != nil {
		authConfig.OIDC = &auth.OIDCConfig{
			Endpoint:     cfg.Auth.OIDC.Endpoint,
			ClientID:     cfg.Auth.OIDC.ClientID,
			ClientSecret: cfg.Auth.OIDC.ClientSecret,
			RedirectURL:  cfg.Auth.OIDC.RedirectURL,
			Scopes:       cfg.Auth.OIDC.Scopes,
		}
	}

	log.Printf("🔑 Using auth provider: %s", cfg.Auth.Provider)
	if cfg.Auth.Provider == "authentik" && cfg.Auth.OIDC != nil {
		log.Printf("🔐 Using Authentik OIDC provider: %s", cfg.Auth.OIDC.Endpoint)
	}

	authService, err := auth.NewAuthService(authConfig)
	if err != nil {
		log.Fatalf("Failed to initialize authentication service: %v", err)
	}

	// Initialize content client for site manager
	contentClient := engine.NewDatabaseClient(database)

	// Initialize site manager with auth provider
	authProvider := &engine.AuthProvider{Type: cfg.Auth.Provider}
	siteManager := content.NewSiteManagerWithAuth(contentClient, cfg.Auth.DevMode, authProvider)

	// Convert config sites to legacy format and register
	var legacySites []*content.SiteConfig
	for _, site := range cfg.Server.Sites {
		legacySite := &content.SiteConfig{
			SiteID:      site.SiteID,
			Path:        site.Path,
			SourcePath:  site.SourcePath,
			Domain:      site.Domain,
			AutoEnhance: site.AutoEnhance,
		}
		if site.Discovery != nil {
			legacySite.Discovery = &content.DiscoveryConfig{
				Enabled:    site.Discovery.Enabled,
				Aggressive: site.Discovery.Aggressive,
				Containers: site.Discovery.Containers,
				Individual: site.Discovery.Individual,
			}
		}
		legacySites = append(legacySites, legacySite)
	}

	if len(legacySites) > 0 {
		if err := siteManager.RegisterSites(legacySites); err != nil {
			log.Printf("⚠️ Failed to register some sites: %v", err)
		}
	}

	// Auto-enhance sites if enabled
	if cfg.Auth.DevMode {
		log.Printf("🔄 Auto-enhancing sites in development mode...")
		if err := siteManager.EnhanceAllSites(); err != nil {
			log.Printf("⚠️ Some sites failed to enhance: %v", err)
		}
	}

	// Initialize handlers
	contentHandler := api.NewContentHandler(database, authService)
	contentHandler.SetSiteManager(siteManager)

	// Setup Chi router
	router := chi.NewRouter()

	// Add Chi middleware
	router.Use(middleware.Logger)
	router.Use(middleware.Recoverer)
	router.Use(cors.Handler(cors.Options{
		AllowedOrigins:   []string{"*"}, // In dev mode, allow all origins
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"*"},
		ExposedHeaders:   []string{"*"},
		AllowCredentials: true,
	}))

	// Health check
	router.Get("/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	})

	// Authentication routes
	router.Route("/auth", func(r chi.Router) {
		r.Post("/login", authService.HandleOAuthLogin)
		r.Get("/callback", authService.HandleOAuthCallback)
	})

	// Content API routes
	router.Route("/api", func(r chi.Router) {
		// Public routes
		r.Get("/content/{siteID}/{id}", contentHandler.GetContent)
		r.Get("/content/{siteID}", contentHandler.GetAllContent)

		// Protected routes (require authentication)
		r.Group(func(r chi.Router) {
			r.Use(authService.RequireAuth)
			r.Post("/content/{siteID}", contentHandler.CreateContent)
			r.Put("/content/{siteID}/{id}", contentHandler.UpdateContent)
			r.Delete("/content/{siteID}/{id}", contentHandler.DeleteContent)

			// Version management
			r.Get("/content/{siteID}/{id}/versions", contentHandler.GetContentVersions)
			r.Post("/content/{siteID}/{id}/rollback/{version}", contentHandler.RollbackContent)
		})
	})

	// Serve static sites
	for _, siteConfig := range siteManager.GetAllSites() {
		log.Printf("📁 Serving site %s from %s at /sites/%s/", siteConfig.SiteID, siteConfig.Path, siteConfig.SiteID)

		// Create a file server for each site
		fileServer := http.FileServer(http.Dir(siteConfig.Path))

		// Handle both /sites/{siteID}/ and /{siteID}/ patterns
		router.Mount(fmt.Sprintf("/sites/%s/", siteConfig.SiteID), http.StripPrefix(fmt.Sprintf("/sites/%s/", siteConfig.SiteID), fileServer))

		// Optionally serve at root for primary site
		if siteConfig.Domain != "" {
			log.Printf("🌐 Site %s available at domain: %s", siteConfig.SiteID, siteConfig.Domain)
		}
	}

	// Catch-all for serving sites by domain or default
	router.NotFound(func(w http.ResponseWriter, r *http.Request) {
		// Try to match by domain first
		host := strings.Split(r.Host, ":")[0] // Remove port if present
		for _, siteConfig := range siteManager.GetAllSites() {
			if siteConfig.Domain == host {
				fileServer := http.FileServer(http.Dir(siteConfig.Path))
				fileServer.ServeHTTP(w, r)
				return
			}
		}

		// Default 404
		http.NotFound(w, r)
	})

	// Start server
	addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
	log.Printf("🚀 Server starting on %s", addr)
	log.Printf("📝 Content API available at http://%s/api/content/{site_id}", addr)
	log.Printf("🔐 Authentication at http://%s/auth/login", addr)

	// Graceful shutdown
	go func() {
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
		<-sigChan
		log.Printf("🛑 Shutting down server...")
		os.Exit(0)
	}()

	if err := http.ListenAndServe(addr, router); err != nil {
		log.Fatalf("Server failed to start: %v", err)
	}
}
