package controller

import (
	"context"

	"dataservices/commons/config"
	"dataservices/internal/entity"

	"github.com/tidbcloud/dataservices/idl/pbgen/proto/dataapi"
)

type ExecutorDispatchStrategy interface {
	// CreateSession is intentionally not part of this strategy. The current code
	// only needs dispatch indirection for ExecSql/CloseSession, while CreateSession
	// still goes through the default executor client path.
	PrepareExecReq(ctx context.Context, req *dataapi.BasicEndpointReqV2) error
	PrepareCloseReq(ctx context.Context, req *dataapi.CloseSessionReq) error
	ExecCallOptions(req *dataapi.BasicEndpointReqV2) []Option
	CloseCallOptions(req *dataapi.CloseSessionReq) []Option
}

// RedisSessionDispatchStrategy keeps the legacy AWS behavior where gateway may
// need session-backed executor routing and load-balancing across executor pods.
type RedisSessionDispatchStrategy struct{}

// SingleExecutorDispatchStrategy is the AliCloud V2 temporary path. It assumes
// the regional executor service resolves to exactly one executor instance, so
// gateway can call executor directly without Redis-backed session routing,
// endpoint lookup, or load-balance hints.
type SingleExecutorDispatchStrategy struct{}

func NewExecutorDispatchStrategy() ExecutorDispatchStrategy {
	if config.Global().IsAliCloudRegionalMode() {
		// Temporary AliCloud assumption: one executor instance per region.
		return SingleExecutorDispatchStrategy{}
	}
	return RedisSessionDispatchStrategy{}
}

func (RedisSessionDispatchStrategy) PrepareExecReq(ctx context.Context, req *dataapi.BasicEndpointReqV2) error {
	if req.SessionId == "" {
		return nil
	}
	return fillSessionForExecReq(ctx, req)
}

func (RedisSessionDispatchStrategy) PrepareCloseReq(ctx context.Context, req *dataapi.CloseSessionReq) error {
	return fillSessionForCloseReq(ctx, req)
}

func (RedisSessionDispatchStrategy) ExecCallOptions(req *dataapi.BasicEndpointReqV2) []Option {
	return []Option{
		WithEndpointAddr(req.SessionItem.GetExecutorEndpoint()),
		WithLoadBalance(req.Source == entity.DataAPISourceExecEndpoint),
	}
}

func (RedisSessionDispatchStrategy) CloseCallOptions(req *dataapi.CloseSessionReq) []Option {
	return []Option{
		WithEndpointAddr(req.SessionItem.GetExecutorEndpoint()),
	}
}

func (SingleExecutorDispatchStrategy) PrepareExecReq(ctx context.Context, req *dataapi.BasicEndpointReqV2) error {
	return nil
}

func (SingleExecutorDispatchStrategy) PrepareCloseReq(ctx context.Context, req *dataapi.CloseSessionReq) error {
	return nil
}

func (SingleExecutorDispatchStrategy) ExecCallOptions(req *dataapi.BasicEndpointReqV2) []Option {
	return nil
}

func (SingleExecutorDispatchStrategy) CloseCallOptions(req *dataapi.CloseSessionReq) []Option {
	return nil
}
