package middleware

import (
	"context"
	"log/slog"
	"runtime/debug"

	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// NewGRPCServerOptions returns gRPC server options with all middleware configured
func NewGRPCServerOptions() []grpc.ServerOption {
	return []grpc.ServerOption{
		grpc.ChainUnaryInterceptor(NewUnaryServerInterceptors()...),
		grpc.ChainStreamInterceptor(NewStreamServerInterceptors()...),
	}
}

// NewUnaryServerInterceptors returns a list of unary server interceptors
func NewUnaryServerInterceptors() []grpc.UnaryServerInterceptor {
	return []grpc.UnaryServerInterceptor{
		logging.UnaryServerInterceptor(NewSlogAdapter()),
		recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(customRecoveryHandler)),
	}
}

// NewStreamServerInterceptors returns a list of stream server interceptors
func NewStreamServerInterceptors() []grpc.StreamServerInterceptor {
	return []grpc.StreamServerInterceptor{
		logging.StreamServerInterceptor(NewSlogAdapter()),
		recovery.StreamServerInterceptor(recovery.WithRecoveryHandler(customRecoveryHandler)),
	}
}

// customRecoveryHandler handles panics in gRPC handlers
func customRecoveryHandler(p any) (err error) {
	slog.Error("panic occurred in gRPC handler",
		"panic", p,
		"stack", string(debug.Stack()))

	return status.Errorf(codes.Internal, "internal server error")
}

// SlogAdapter adapts slog.Logger to logging.Logger interface
type SlogAdapter struct {
}

// NewSlogAdapter creates a new SlogAdapter
func NewSlogAdapter() logging.Logger {
	return &SlogAdapter{}
}

// Log implements the logging.Logger interface
func (s *SlogAdapter) Log(ctx context.Context, level logging.Level, msg string, fields ...any) {
	slogLevel := slog.Level(level)

	slog.Log(ctx, slogLevel, msg, fields...)
}
