package grpctool

import (
	"context"
	"log/slog"
	"time"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/errz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/fieldz"
	"google.golang.org/grpc"
)

// PoolSelf is a decorator that uses an in-memory connection to dial self rather than going over network.
type PoolSelf[T Target] struct {
	log      *slog.Logger
	errRep   errz.ErrReporter
	delegate PoolInterface[T]
	selfURL  T
	conn     selfPoolConn
}

func NewPoolSelf[T Target](log *slog.Logger, errRep errz.ErrReporter, delegate PoolInterface[T], selfURL T, selfConn *grpc.ClientConn) *PoolSelf[T] {
	return &PoolSelf[T]{
		log:      log,
		errRep:   errRep,
		delegate: delegate,
		selfURL:  selfURL,
		conn: selfPoolConn{
			ClientConn: selfConn,
		},
	}
}

func (p *PoolSelf[T]) Dial(targetURL T) (PoolConn, error) {
	if targetURL == p.selfURL {
		return &p.conn, nil
	}
	return p.delegate.Dial(targetURL)
}

func (p *PoolSelf[T]) Shutdown(deadline time.Duration) {
	p.delegate.Shutdown(deadline)
	err := p.conn.Close()
	if err != nil {
		p.errRep.HandleProcessingError(context.Background(), p.log, "Failed to close connection",
			err, fieldz.New("conn_target", p.conn.CanonicalTarget()))
	}
}

type selfPoolConn struct {
	*grpc.ClientConn
}

func (s *selfPoolConn) Done() {
	// noop
}
