// @title Inference Service API
// @version 1.0
// @description A simple embedding service that wraps AWS Bedrock and provides OpenAI-compatible API endpoints.
// @host localhost:8080
// @BasePath /
package app

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

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"

	"github.com/tidbcloud/inference-service/internal/config"
	"github.com/tidbcloud/inference-service/internal/router"
	"github.com/tidbcloud/inference-service/internal/service"
	"github.com/tidbcloud/inference-service/internal/version"
)

// Server represents the HTTP server
type Server struct {
	httpServer     *http.Server
	metricsServer  *http.Server
	bedrockService *service.BedrockService
	usageService   *service.UsageService
}

// NewServer creates a new server instance
func NewServer() *Server {
	return &Server{}
}

// Setup initializes the server components
func (s *Server) Setup() error {
	buildInfo := version.GetBuildInfo()
	zap.L().Info("Starting inference service",
		zap.Any("buildInfo", buildInfo),
		zap.Any("config", config.Global().ForPrint()))

	// Initialize services
	s.bedrockService = service.NewBedrockService(
		config.Global().EmbeddingProviders.AWSBedrock.APIToken,
		config.Global().EmbeddingProviders.AWSBedrock.Region,
	)

	var err error
	s.usageService, err = service.NewUsageService()
	if err != nil {
		return fmt.Errorf("failed to initialize usage service: %w", err)
	}

	zap.L().Info("Server setup completed successfully")
	return nil
}

// ServeHTTP starts the HTTP server
func (s *Server) ServeHTTP() error {
	// Setup HTTP router
	if config.Global().Server.LogLevel != "debug" {
		gin.SetMode(gin.ReleaseMode)
	}

	r := router.NewRouter(s.bedrockService, s.usageService)

	// Create HTTP server
	s.httpServer = &http.Server{
		Addr:    config.Global().Server.HTTPListenAddr,
		Handler: r,
	}

	// Start metrics server
	go s.startMetricsServer()

	// Start HTTP server in goroutine
	go func() {
		zap.L().Info("Starting HTTP server", zap.String("addr", s.httpServer.Addr))
		if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			zap.L().Fatal("HTTP server failed", zap.Error(err))
		}
	}()

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

	zap.L().Info("Shutting down server...")

	// Graceful shutdown
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Shutdown HTTP server
	if err := s.httpServer.Shutdown(ctx); err != nil {
		zap.L().Error("HTTP server forced to shutdown", zap.Error(err))
	}

	// Shutdown metrics server
	if s.metricsServer != nil {
		if err := s.metricsServer.Shutdown(ctx); err != nil {
			zap.L().Error("Metrics server forced to shutdown", zap.Error(err))
		}
	}

	// Close usage service (saves data)
	if err := s.usageService.Close(); err != nil {
		zap.L().Error("Failed to close usage service", zap.Error(err))
	}

	zap.L().Info("Server shutdown completed")
	return nil
}

// startMetricsServer starts the Prometheus metrics server
func (s *Server) startMetricsServer() {
	mux := http.NewServeMux()
	mux.Handle("/metrics", promhttp.Handler())

	s.metricsServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", config.Global().Server.MetricsPort),
		Handler: mux,
	}

	zap.L().Info("Starting metrics server", zap.Int("port", config.Global().Server.MetricsPort))
	if err := s.metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		zap.L().Error("Metrics server failed", zap.Error(err))
	}
}
