package service

import (
	"context"
	"net/http"
	"sync"
	"time"

	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/cluster-service-ng/internal/cluster-agent/crypto"
	"github.com/tidbcloud/cluster-service-ng/internal/cluster-agent/internal/objectstore"
)

// MetricsClient defines the interface for fetching pod metrics from metrics-server
type MetricsClient interface {
	// GetPodCPUAndMemory returns CPU cores used and memory bytes used for a pod
	// Returns (cpuCores, memoryBytes, error)
	GetPodCPUAndMemory(ctx context.Context, namespace, podName string) (float64, uint64, error)
}

type AgentService struct {
	dataplaneCli client.Client
	metricsCli   MetricsClient

	// tikvHTTPClient is the shared mTLS HTTP client for calling TiKV status port.
	// It is built once at startup from /client-ssl/ mount. If nil, TiKV metrics
	// proxy is unavailable (503) but all other endpoints remain functional.
	tikvHTTPClient *http.Client

	// nodeExporterHTTPClient is the plain HTTP client for reaching node-exporter on the
	// host network. Never nil after NewAgentService; defaults to a 10-second timeout client.
	nodeExporterHTTPClient *http.Client

	nodeRestarter NodeRestartProvider
	podRestarter  PodRestartProvider

	tiProxyHealthAPIProber TiProxyHealthAPIProber

	encryptor *crypto.Encryptor

	// provider "aws", "gcp"
	provider          string
	planReplayerStore objectstore.Store

	// resolveV2HTTPDeps overrides the default V2 HTTP dependency resolver (used in tests).
	resolveV2HTTPDeps v2HTTPDepsResolverFunc

	// resolveWorkerHTTPDeps overrides the worker TiDB V2 HTTP dependency resolver (used in tests).
	resolveWorkerHTTPDeps workerV2HTTPDepsResolverFunc

	// if a request is being processed to activate the privileged TiDB for a cluster,
	// other requests to activate the privileged TiDB must wait until the ongoing one finishes.
	mutexPrivilegedTiDBActivation sync.Mutex

	// privilegedTiDBDeactivationWait controls how long to delay before deleting the
	// privileged TiDB pod after a SQL exec. Zero means use the default.
	privilegedTiDBDeactivationWait time.Duration
}

const defaultPrivilegedTiDBDeactivationWait = 30 * time.Second

type Option func(*AgentService)

func WithProvider(provider string) Option {
	return func(as *AgentService) {
		as.provider = provider
	}
}

// WithPlanReplayerStore enables object-storage-backed Plan Replayer APIs.
func WithPlanReplayerStore(store objectstore.Store) Option {
	return func(as *AgentService) {
		as.planReplayerStore = store
	}
}

func WithNodeRestarter(restarter NodeRestartProvider) Option {
	return func(as *AgentService) {
		as.nodeRestarter = restarter
	}
}

func WithMetricsClient(client MetricsClient) Option {
	return func(as *AgentService) {
		as.metricsCli = client
	}
}

func WithTiKVHTTPClient(c *http.Client) Option {
	return func(as *AgentService) {
		as.tikvHTTPClient = c
	}
}

func WithNodeExporterHTTPClient(c *http.Client) Option {
	return func(as *AgentService) {
		as.nodeExporterHTTPClient = c
	}
}

func WithTiProxyHealthAPIProber(prober TiProxyHealthAPIProber) Option {
	return func(as *AgentService) {
		as.tiProxyHealthAPIProber = prober
	}
}

// WithPrivilegedTiDBDeactivationWait overrides the delay between a privileged
// TiDB SQL exec and the subsequent pod deletion. Non-positive values are
// treated as "use default".
func WithPrivilegedTiDBDeactivationWait(d time.Duration) Option {
	return func(as *AgentService) {
		as.privilegedTiDBDeactivationWait = d
	}
}

func NewAgentService(
	cli client.Client,
	encryptor *crypto.Encryptor,
	opts ...Option) *AgentService {
	as := &AgentService{
		dataplaneCli:           cli,
		encryptor:              encryptor,
		nodeExporterHTTPClient: &http.Client{Timeout: 10 * time.Second},
	}
	for _, opt := range opts {
		opt(as)
	}
	if as.privilegedTiDBDeactivationWait <= 0 {
		as.privilegedTiDBDeactivationWait = defaultPrivilegedTiDBDeactivationWait
	}
	if as.tiProxyHealthAPIProber == nil {
		as.tiProxyHealthAPIProber = &httpsTiProxyHealthAPIProber{}
	}
	as.podRestarter = NewPodRestarter()
	return as
}
