// Package grpcserver implements the MCR gRPC admin API server.
//
// It provides the same business logic as the REST admin API in
// internal/server/, using shared internal/db and internal/gc packages.
// The server enforces TLS 1.3 minimum, auth via MCIAS token validation,
// and admin role checks on privileged RPCs.
package grpcserver

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"sync"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	pb "git.wntrmute.dev/kyle/mcr/gen/mcr/v1"
	"git.wntrmute.dev/kyle/mcr/internal/db"
	"git.wntrmute.dev/kyle/mcr/internal/gc"
	"git.wntrmute.dev/kyle/mcr/internal/policy"
	"git.wntrmute.dev/kyle/mcr/internal/server"
)

// AuditFunc is a callback for recording audit events. It follows the same
// signature as db.WriteAuditEvent but without an error return -- audit
// failures should not block request processing.
type AuditFunc func(eventType, actorID, repository, digest, ip string, details map[string]string)

// Deps holds the dependencies injected into the gRPC server.
type Deps struct {
	DB        *db.DB
	Validator server.TokenValidator
	Engine    PolicyReloader
	AuditFn   AuditFunc
	Collector *gc.Collector
}

// PolicyReloader can reload policy rules from a store.
type PolicyReloader interface {
	Reload(store policy.RuleStore) error
}

// GCStatus tracks the current state of garbage collection for the gRPC server.
type GCStatus struct {
	mu      sync.Mutex
	running bool
	lastRun *gcLastRun
}

type gcLastRun struct {
	StartedAt    string
	CompletedAt  string
	BlobsRemoved int
	BytesFreed   int64
}

// Server wraps a grpc.Server with MCR-specific configuration.
type Server struct {
	gs       *grpc.Server
	deps     Deps
	gcStatus *GCStatus
}

// New creates a configured gRPC server with the interceptor chain:
// [Request Logger] -> [Auth Interceptor] -> [Admin Interceptor] -> [Handler]
//
// The TLS config enforces TLS 1.3 minimum. If certFile or keyFile is
// empty, the server is created without TLS (for testing only).
func New(certFile, keyFile string, deps Deps) (*Server, error) {
	authInt := newAuthInterceptor(deps.Validator)
	adminInt := newAdminInterceptor()

	chain := grpc.ChainUnaryInterceptor(
		loggingInterceptor,
		authInt.unary,
		adminInt.unary,
	)

	var opts []grpc.ServerOption
	opts = append(opts, chain)

	// Configure TLS if cert and key are provided.
	if certFile != "" && keyFile != "" {
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return nil, fmt.Errorf("grpcserver: load TLS cert: %w", err)
		}
		tlsCfg := &tls.Config{
			Certificates: []tls.Certificate{cert},
			MinVersion:   tls.VersionTLS13,
		}
		opts = append(opts, grpc.Creds(credentials.NewTLS(tlsCfg)))
	}

	// The JSON codec is registered globally via init() in gen/mcr/v1/codec.go.
	// The client must use grpc.ForceCodecV2(mcrv1.JSONCodec{}) to match.
	_ = pb.JSONCodec{} // ensure the gen/mcr/v1 init() runs (codec registration)

	gs := grpc.NewServer(opts...)

	gcStatus := &GCStatus{}

	s := &Server{gs: gs, deps: deps, gcStatus: gcStatus}

	// Register all services.
	pb.RegisterRegistryServiceServer(gs, &registryService{
		db:        deps.DB,
		collector: deps.Collector,
		gcStatus:  gcStatus,
		auditFn:   deps.AuditFn,
	})
	pb.RegisterPolicyServiceServer(gs, &policyService{
		db:      deps.DB,
		engine:  deps.Engine,
		auditFn: deps.AuditFn,
	})
	pb.RegisterAuditServiceServer(gs, &auditService{
		db: deps.DB,
	})
	pb.RegisterAdminServiceServer(gs, &adminService{})

	return s, nil
}

// Serve starts the gRPC server on the given listener.
func (s *Server) Serve(lis net.Listener) error {
	log.Printf("grpc server listening on %s", lis.Addr())
	return s.gs.Serve(lis)
}

// GracefulStop gracefully stops the gRPC server.
func (s *Server) GracefulStop() {
	s.gs.GracefulStop()
}

// GRPCServer returns the underlying grpc.Server for testing.
func (s *Server) GRPCServer() *grpc.Server {
	return s.gs
}
