package main

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

	"github.com/google/uuid"

	"qycw/go-server/internal/bootstrap"
	"qycw/go-server/internal/config"
	httpserver "qycw/go-server/internal/http"
	"qycw/go-server/internal/license"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))

	cfg, err := config.LoadBootstrap(os.Args[1:])
	if err != nil {
		logger.Error("load bootstrap config failed", "error", err)
		os.Exit(2)
	}

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

	app, err := bootstrap.New(ctx, cfg, logger)
	if err != nil {
		logger.Error("bootstrap failed", "error", err)
		os.Exit(1)
	}
	defer app.Close()

	server := httpserver.NewServer(cfg, app, logger)
	if app.LicenseManager != nil {
		if err := app.LicenseManager.InitEvaluator(ctx); err != nil {
			if errors.Is(err, license.ErrReactivationRequired) {
				logger.Warn("instance token rejected; starting API in activation-only mode", "error", err)
			} else {
				logger.Error("online license validation failed, cannot start safely", "error", err)
				os.Exit(1)
			}
		}
	}
	reporter := license.NewReporter(app.LicenseManager, cfg.LicenseHeartbeatInterval, logger)
	if app.Redis != nil {
		reporter = reporter.WithRedis(app.Redis, "api-"+uuid.NewString())
	}
	go func() {
		if err := reporter.Run(ctx); err != nil {
			logger.Error("authorization reporter stopped", "error", err)
			stop()
		}
	}()

	go func() {
		logger.Info("go api server listening", "addr", cfg.HTTPAddr)
		if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			logger.Error("api server failed", "error", err)
			stop()
		}
	}()

	<-ctx.Done()
	logger.Info("shutdown requested")

	shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
	defer cancel()

	if err := server.Shutdown(shutdownCtx); err != nil {
		logger.Error("api server shutdown failed", "error", err)
		os.Exit(1)
	}

	time.Sleep(50 * time.Millisecond)
	logger.Info("api server stopped")
}
