package server

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"net/http"
	"time"

	"golang.org/x/net/http2"

	"yobble-gateway-go/internal/config"
	"yobble-gateway-go/internal/logger"
	"yobble-gateway-go/internal/middleware"
	"yobble-gateway-go/internal/proxy"
	"yobble-gateway-go/pkg/geoip"
)

// Server обертка для http.Server с дополнительными полями.
type Server struct {
	httpServer *http.Server
	log        *slog.Logger
}

// NewServer создает новый экземпляр Server.
func NewServer(cfg *config.Settings, geoIPService *geoip.GeoIPService) *Server {
	op := "server.NewServer"
	log := logger.NewLoggerWithOp(op)

	// Create the main proxy handler
	proxyHandler := proxy.NewProxyHandler(cfg, geoIPService)

	// Apply middleware chain
	chain := middleware.RemoveTrailingSlashMiddleware(
		middleware.RealIPMiddleware(
			proxyHandler,
		),
	)

	addr := fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port)

	httpServer := &http.Server{
		Addr:    addr,
		Handler: chain, // Use the chained middleware as the main handler
		// TODO: Add ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout from config
	}

	// Explicitly enable HTTP/2
	http2.ConfigureServer(httpServer, nil)

	return &Server{
		httpServer: httpServer,
		log:        log,
	}
}

// Start запускает HTTP/HTTPS сервер в отдельной горутине.
// Он возвращает канал, который будет закрыт, когда сервер завершит работу.
func (s *Server) Start(fullchain, privkey string) <-chan error {
	errChan := make(chan error, 1)

	s.log.Info("server starting with TLS and HTTP/2", slog.String("address", s.httpServer.Addr))

	go func() {
		defer close(errChan)
		if err := s.httpServer.ListenAndServeTLS(fullchain, privkey); err != nil && !errors.Is(err, http.ErrServerClosed) {
			s.log.Error("server failed to listen and serve", slog.Any("error", err))
			errChan <- err
		}
		s.log.Info("server stopped listening")
	}()

	return errChan
}

// Shutdown gracefully shuts down the server.
func (s *Server) Shutdown(ctx context.Context) error {
	s.log.Info("server is shutting down gracefully")

	// Give the server a grace period to finish active connections
	shutdownCtx, cancel := context.WithTimeout(ctx, 30*time.Second) // TODO: Make timeout configurable
	defer cancel()

	if err := s.httpServer.Shutdown(shutdownCtx); err != nil {
		s.log.Error("server shutdown failed", slog.Any("error", err))
		return err
	}

	s.log.Info("server shutdown complete")
	return nil
}
