// Command validator is the home-side Telegram validator. It serves the validation
// gRPC API (pkg/proto/telegram/v1 ValidateInitData/ValidateLoginWidget) the gateway
// calls during Telegram auth. It holds the bot token only as the HMAC secret and
// never reaches the Telegram Bot API, so it runs on the main host with no VPN and
// no Telegram egress, and game login stays independent of the remote bot. See
// platform/telegram/README.md.
package main

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

	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
	"go.uber.org/zap"
	"google.golang.org/grpc"

	telegramv1 "scrabble/pkg/proto/telegram/v1"
	pkgtel "scrabble/pkg/telemetry"
	"scrabble/platform/telegram/internal/config"
	"scrabble/platform/telegram/internal/initdata"
	"scrabble/platform/telegram/internal/loginwidget"
	"scrabble/platform/telegram/internal/validator"
)

// telemetryShutdownTimeout bounds the OpenTelemetry flush during process exit.
const telemetryShutdownTimeout = 5 * time.Second

func main() {
	cfg, err := config.LoadValidator()
	if err != nil {
		log.Fatalf("validator: load config: %v", err)
	}
	logger, err := newLogger(cfg.LogLevel)
	if err != nil {
		log.Fatalf("validator: build logger: %v", err)
	}
	defer func() { _ = logger.Sync() }()

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

	if err := run(ctx, cfg, logger); err != nil {
		logger.Fatal("validator: terminated", zap.Error(err))
	}
}

// run wires the validator gRPC server and serves it until the context is cancelled.
func run(ctx context.Context, cfg config.ValidatorConfig, logger *zap.Logger) error {
	tel, err := pkgtel.New(ctx, cfg.Telemetry)
	if err != nil {
		return err
	}
	defer func() {
		shutdownCtx, cancel := context.WithTimeout(context.Background(), telemetryShutdownTimeout)
		defer cancel()
		if err := tel.Shutdown(shutdownCtx); err != nil {
			logger.Warn("telemetry shutdown", zap.Error(err))
		}
	}()
	if err := tel.StartRuntimeMetrics(); err != nil {
		logger.Warn("telemetry: start runtime metrics", zap.Error(err))
	}

	srv := validator.NewServer(initdata.NewHMACValidator(cfg.Token), loginwidget.NewHMACValidator(cfg.Token))
	grpcServer := grpc.NewServer(grpc.StatsHandler(otelgrpc.NewServerHandler()))
	telegramv1.RegisterTelegramServer(grpcServer, srv)

	lis, err := net.Listen("tcp", cfg.GRPCAddr)
	if err != nil {
		return err
	}

	go func() {
		<-ctx.Done()
		grpcServer.GracefulStop()
	}()

	logger.Info("telegram validator starting", zap.String("grpc_addr", cfg.GRPCAddr))
	if err := grpcServer.Serve(lis); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
		return err
	}
	return nil
}

// newLogger builds a production JSON logger at the given level.
func newLogger(level string) (*zap.Logger, error) {
	var lvl zap.AtomicLevel
	if err := lvl.UnmarshalText([]byte(level)); err != nil {
		return nil, err
	}
	cfg := zap.NewProductionConfig()
	cfg.Level = lvl
	return cfg.Build()
}
