package main

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

	"hfb_sys/backend/internal/config"
	"hfb_sys/backend/internal/database"
	"hfb_sys/backend/internal/jobs/ordertimeout"
	"hfb_sys/backend/internal/logging"
	"hfb_sys/backend/internal/router"

	"go.uber.org/zap"
)

// @title           HFB Sys API
// @version         1.0
// @description     哈夫币租号平台 API 文档
// @termsOfService  http://swagger.io/terms/

// @contact.name   API Support
// @contact.email  support@hfb.com

// @license.name  MIT
// @license.url   https://opensource.org/licenses/MIT

// @host      localhost:8080
// @BasePath  /api

// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.

func main() {
	cfg := config.Load()
	logger, err := logging.New(cfg.Log)
	if err != nil {
		panic(err)
	}
	defer func() {
		_ = logger.Sync()
	}()

	var deps router.Dependencies
	db, err := database.OpenMySQL(cfg.MySQLDSN)
	if err != nil {
		logger.Warn("mysql unavailable; database-backed APIs will return 503", zap.Error(err))
	} else {
		deps.DB = db
	}
	redisClient, err := database.OpenRedis(context.Background(), database.RedisConfig{
		Addr:     cfg.RedisAddr,
		Password: cfg.RedisPassword,
		DB:       cfg.RedisDB,
	})
	if err != nil {
		logger.Warn("redis unavailable; redis-backed APIs will return 503", zap.Error(err))
	} else {
		deps.Redis = redisClient
		defer func() {
			_ = redisClient.Close()
		}()
	}

	engine := router.New(cfg, deps, logger)
	jobCtx, stopJobs := context.WithCancel(context.Background())
	defer stopJobs()
	if deps.DB != nil {
		ordertimeout.New(deps.DB, logger).Start(jobCtx)
	}
	server := &http.Server{
		Addr:              cfg.AppAddr,
		Handler:           engine,
		ReadHeaderTimeout: 5 * time.Second,
	}

	go func() {
		logger.Info("api server starting", zap.String("addr", cfg.AppAddr))
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Fatal("api server failed", zap.Error(err))
		}
	}()

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

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := server.Shutdown(ctx); err != nil {
		logger.Fatal("api server shutdown failed", zap.Error(err))
	}
	logger.Info("api server stopped")
}
