package daemon

import (
	"context"
	"strings"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
)

func NewServer(startedService *StartedService, secret string) *grpc.Server {
	server := grpc.NewServer(
		grpc.ChainUnaryInterceptor(newUnaryAuthInterceptor(secret), UnaryLocaleInterceptor),
		grpc.ChainStreamInterceptor(newStreamAuthInterceptor(secret), StreamLocaleInterceptor),
	)
	healthServer := health.NewServer()
	RegisterStartedServiceServer(server, startedService)
	healthServer.SetServingStatus(StartedService_ServiceDesc.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
	grpc_health_v1.RegisterHealthServer(server, healthServer)
	reflection.Register(server)
	return server
}

func newUnaryAuthInterceptor(secret string) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		err := authenticate(ctx, secret)
		if err != nil {
			return nil, err
		}
		return handler(ctx, request)
	}
}

func newStreamAuthInterceptor(secret string) grpc.StreamServerInterceptor {
	return func(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		err := authenticate(stream.Context(), secret)
		if err != nil {
			return err
		}
		return handler(server, stream)
	}
}

func authenticate(ctx context.Context, secret string) error {
	if secret == "" {
		return nil
	}
	md, loaded := metadata.FromIncomingContext(ctx)
	if !loaded {
		return status.Error(codes.Unauthenticated, "missing metadata")
	}
	values := md.Get("authorization")
	if len(values) == 0 {
		return status.Error(codes.Unauthenticated, "missing authorization")
	}
	token, isBearer := strings.CutPrefix(values[0], "Bearer ")
	if !isBearer || token != secret {
		return status.Error(codes.Unauthenticated, "invalid authorization")
	}
	return nil
}
