package main

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

	"github.com/tdvorak/seen/backend/internal/api"
	"github.com/tdvorak/seen/backend/internal/api/handlers"
	"github.com/tdvorak/seen/backend/internal/config"
	"github.com/tdvorak/seen/backend/internal/downloader"
	"github.com/tdvorak/seen/backend/internal/integrations/cache"
	"github.com/tdvorak/seen/backend/internal/integrations/igdb"
	"github.com/tdvorak/seen/backend/internal/repositories/postgres"
	"github.com/tdvorak/seen/backend/internal/scanner"
	"github.com/tdvorak/seen/backend/internal/services/auth"
	"github.com/tdvorak/seen/backend/internal/services/catalog"
	"github.com/tdvorak/seen/backend/internal/services/download"
	"github.com/tdvorak/seen/backend/internal/workers"
	"github.com/tdvorak/seen/backend/pkg/logger"
	"go.uber.org/zap"
)

func main() {
	cfg := config.Load()
	log := logger.New(cfg.Env)
	defer func() {
		_ = log.Sync()
	}()

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	pgPool, err := postgres.NewPool(ctx, cfg.Postgres, log)
	if err != nil {
		log.Fatal("postgres startup failed", zap.Error(err))
	}
	defer pgPool.Close()

	cacheManager, err := cache.NewManager(ctx, cfg.Cache, log)
	if err != nil {
		log.Fatal("dragonfly startup failed", zap.Error(err))
	}
	defer func() {
		if err := cacheManager.Close(); err != nil {
			log.Error("failed to close cache manager", zap.Error(err))
		}
	}()

	// Warmup cache with common data
	if err := cacheManager.WarmupCache(ctx); err != nil {
		log.Warn("cache warmup failed", zap.Error(err))
	}

	authRepository := postgres.NewAuthRepository(pgPool)
	catalogRepository := postgres.NewCatalogRepository(pgPool)
	downloadRepository := postgres.NewDownloadRepository(pgPool)

	authService := auth.NewService(authRepository, cfg.Auth, log)
	catalogService := catalog.NewService(catalogRepository)
	downloadService := download.NewService(downloadRepository)
	igdbClient := igdb.NewClient(cfg.IGDB)
	if igdbClient.Enabled() {
		catalogService.SetGameLookup(igdbClient)
		log.Info("igdb live search enabled")
	}

	healthHandler := handlers.NewHealthHandler(pgPool, cacheManager.Client())
	authHandler := handlers.NewAuthHandler(authService)
	catalogHandler := handlers.NewCatalogHandler(catalogService, authService)
	downloadHandler := handlers.NewDownloadHandler(downloadService, authService)
	placeholderHandler := handlers.NewPlaceholderHandler()

	router := api.NewRouter(cfg, log, api.Handlers{
		Health:      healthHandler,
		Auth:        authHandler,
		Catalog:     catalogHandler,
		Download:    downloadHandler,
		Placeholder: placeholderHandler,
	})

	workerManager := workers.NewManager(
		log,
		downloader.NewWorker(log),
		scanner.NewWorker(log),
	)
	workerManager.Start(ctx)

	httpServer := &http.Server{
		Addr:         cfg.HTTP.Addr(),
		Handler:      router,
		ReadTimeout:  cfg.HTTP.ReadTimeout,
		WriteTimeout: cfg.HTTP.WriteTimeout,
	}

	go func() {
		log.Info("http server started", zap.String("addr", cfg.HTTP.Addr()))
		if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatal("http server failed", zap.Error(err))
		}
	}()

	<-ctx.Done()
	log.Info("shutdown signal received")

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

	if err := httpServer.Shutdown(shutdownCtx); err != nil {
		log.Error("graceful shutdown failed", zap.Error(err))
	}
}
