// Package api provides gRPC server implementation for Audit Service.
package api

import (
	"context"
	"fmt"
	"net"
	"time"

	auditv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/audit/v1"
	"git.dcentral.systems/toolz/goplt/pkg/config"
	"git.dcentral.systems/toolz/goplt/pkg/logger"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/reflection"
)

// GRPCServer wraps the gRPC server with lifecycle management.
type GRPCServer struct {
	server   *grpc.Server
	listener net.Listener
	config   config.ConfigProvider
	logger   logger.Logger
	port     int
}

// NewGRPCServer creates a new gRPC server for the Audit Service.
func NewGRPCServer(
	auditService *Server,
	cfg config.ConfigProvider,
	log logger.Logger,
) (*GRPCServer, error) {
	// Get port from config
	port := cfg.GetInt("services.audit.port")
	if port == 0 {
		port = 8084 // Default port for audit service
	}

	// Create listener
	addr := fmt.Sprintf("0.0.0.0:%d", port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, fmt.Errorf("failed to listen on %s: %w", addr, err)
	}

	// Create gRPC server
	grpcServer := grpc.NewServer()

	// Register audit service
	auditv1.RegisterAuditServiceServer(grpcServer, auditService)

	// Register health service
	healthServer := health.NewServer()
	grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
	healthServer.SetServingStatus("audit.v1.AuditService", grpc_health_v1.HealthCheckResponse_SERVING)

	// Register reflection for grpcurl
	reflection.Register(grpcServer)

	return &GRPCServer{
		server:   grpcServer,
		listener: listener,
		config:   cfg,
		logger:   log,
		port:     port,
	}, nil
}

// Start starts the gRPC server.
func (s *GRPCServer) Start() error {
	s.logger.Info("Starting Audit Service gRPC server",
		zap.Int("port", s.port),
		zap.String("addr", s.listener.Addr().String()),
	)

	// Start server in a goroutine
	errChan := make(chan error, 1)
	go func() {
		if err := s.server.Serve(s.listener); err != nil {
			errChan <- err
		}
	}()

	// Wait a bit to check for immediate errors
	select {
	case err := <-errChan:
		return fmt.Errorf("gRPC server failed to start: %w", err)
	case <-time.After(100 * time.Millisecond):
		s.logger.Info("Audit Service gRPC server started successfully",
			zap.Int("port", s.port),
		)
		return nil
	}
}

// Stop gracefully stops the gRPC server.
func (s *GRPCServer) Stop(ctx context.Context) error {
	s.logger.Info("Stopping Audit Service gRPC server")

	// Create a channel for graceful stop
	stopped := make(chan struct{})
	go func() {
		s.server.GracefulStop()
		close(stopped)
	}()

	// Wait for graceful stop or timeout
	select {
	case <-stopped:
		s.logger.Info("Audit Service gRPC server stopped gracefully")
		return nil
	case <-ctx.Done():
		s.logger.Warn("Audit Service gRPC server stop timeout, forcing stop")
		s.server.Stop()
		return ctx.Err()
	}
}

// Port returns the port the server is listening on.
func (s *GRPCServer) Port() int {
	return s.port
}
