package main

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

	"github.com/breakpilot/edu-search-service/internal/api/handlers"
	"github.com/breakpilot/edu-search-service/internal/config"
	"github.com/breakpilot/edu-search-service/internal/database"
	"github.com/breakpilot/edu-search-service/internal/indexer"
	"github.com/breakpilot/edu-search-service/internal/orchestrator"
	"github.com/breakpilot/edu-search-service/internal/search"
	"github.com/breakpilot/edu-search-service/internal/staff"
	"github.com/gin-gonic/gin"
)

func main() {
	log.Println("Starting edu-search-service...")

	// Load configuration
	cfg := config.Load()
	log.Printf("Configuration loaded: Port=%s, OpenSearch=%s, Index=%s",
		cfg.Port, cfg.OpenSearchURL, cfg.IndexName)

	// Initialize OpenSearch indexer client
	indexClient, err := indexer.NewClient(
		cfg.OpenSearchURL,
		cfg.OpenSearchUsername,
		cfg.OpenSearchPassword,
		cfg.IndexName,
	)
	if err != nil {
		log.Fatalf("Failed to create indexer client: %v", err)
	}

	// Create index if not exists
	ctx := context.Background()
	if err := indexClient.CreateIndex(ctx); err != nil {
		log.Printf("Warning: Could not create index (may already exist): %v", err)
	}

	// Initialize search service
	searchService, err := search.NewService(
		cfg.OpenSearchURL,
		cfg.OpenSearchUsername,
		cfg.OpenSearchPassword,
		cfg.IndexName,
	)
	if err != nil {
		log.Fatalf("Failed to create search service: %v", err)
	}

	// Initialize seed store for admin API
	if err := handlers.InitSeedStore(cfg.SeedsDir); err != nil {
		log.Printf("Warning: Could not initialize seed store: %v", err)
	}

	// Create handler
	handler := handlers.NewHandler(cfg, searchService, indexClient)

	// Initialize PostgreSQL for Staff/Publications database
	dbCfg := &database.Config{
		Host:     cfg.DBHost,
		Port:     cfg.DBPort,
		User:     cfg.DBUser,
		Password: cfg.DBPassword,
		DBName:   cfg.DBName,
		SSLMode:  cfg.DBSSLMode,
	}

	db, err := database.New(ctx, dbCfg)
	if err != nil {
		log.Printf("Warning: Could not connect to PostgreSQL for staff database: %v", err)
		log.Println("Staff/Publications features will be disabled")
	} else {
		defer db.Close()
		log.Println("Connected to PostgreSQL for staff/publications database")

		// Run migrations
		if err := db.RunMigrations(ctx); err != nil {
			log.Printf("Warning: Could not run migrations: %v", err)
		}
	}

	// Create repository for Staff handlers (may be nil if DB connection failed)
	var repo *database.Repository
	if db != nil {
		repo = database.NewRepository(db)
	}

	// Setup Gin router
	gin.SetMode(gin.ReleaseMode)
	router := gin.New()
	router.Use(gin.Recovery())
	router.Use(gin.Logger())

	// CORS middleware
	router.Use(func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	})

	// Setup routes
	handlers.SetupRoutes(router, handler, cfg.APIKey)

	// Setup Staff/Publications routes if database is available
	if repo != nil {
		staffHandlers := handlers.NewStaffHandlers(repo, cfg.StaffCrawlerEmail)
		apiV1 := router.Group("/api/v1")
		staffHandlers.RegisterRoutes(apiV1)
		log.Println("Staff/Publications API routes registered")

		// Setup AI Extraction routes for vast.ai integration
		aiHandlers := handlers.NewAIExtractionHandlers(repo)
		aiHandlers.RegisterRoutes(apiV1)
		log.Println("AI Extraction API routes registered")
	}

	// Setup Orchestrator routes if database is available
	if db != nil {
		orchRepo := orchestrator.NewPostgresRepository(db.Pool)

		// Create real crawlers with adapters for orchestrator interface
		staffCrawler := staff.NewStaffCrawler(repo)
		staffAdapter := staff.NewOrchestratorAdapter(staffCrawler, repo)
		pubAdapter := staff.NewPublicationOrchestratorAdapter(repo)

		orch := orchestrator.NewOrchestrator(orchRepo, staffAdapter, pubAdapter)
		orchHandler := handlers.NewOrchestratorHandler(orch, orchRepo)

		v1 := router.Group("/v1")
		v1.Use(handlers.AuthMiddleware(cfg.APIKey))
		handlers.SetupOrchestratorRoutes(v1, orchHandler)
		log.Println("Orchestrator API routes registered")

		// Setup Audience routes (reuses orchRepo which implements AudienceRepository)
		audienceHandler := handlers.NewAudienceHandler(orchRepo)
		handlers.SetupAudienceRoutes(v1, audienceHandler)
		log.Println("Audience API routes registered")
	}

	// Create HTTP server
	srv := &http.Server{
		Addr:         ":" + cfg.Port,
		Handler:      router,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 30 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	// Start server in goroutine
	go func() {
		log.Printf("Server listening on port %s", cfg.Port)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("Server error: %v", err)
		}
	}()

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

	log.Println("Shutting down server...")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := srv.Shutdown(ctx); err != nil {
		log.Fatalf("Server forced to shutdown: %v", err)
	}

	log.Println("Server exited")
}
