package service

import (
	"context"
	"strings"

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

	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"
)

// GKEPolicyControlClient isolates cluster-agent's Policy Control state machine
// from Google APIs, and keeps tests independent from live GCP services.
type GKEPolicyControlClient interface {
	GetProjectIdentity(ctx context.Context, project string) (GKEPolicyControlProjectIdentity, error)
	GetServicesStatus(ctx context.Context, projectID string, services []string) ([]string, []string, error)
	StartEnableServices(ctx context.Context, projectID string, services []string) ([]string, error)
	GetFleetMembership(ctx context.Context, projectID, location, clusterName string) (string, error)
	StartEnableFleet(ctx context.Context, projectID, location, clusterName, fleetProjectID string) ([]string, error)
	GetPolicyController(ctx context.Context, fleetProjectID, membership string) (GKEPolicyControllerStatus, error)
	StartEnablePolicyController(ctx context.Context, fleetProjectID, membership string) (string, []string, error)
}

// GKEPolicyControlProjectIdentity is the canonical identity returned by
// Resource Manager for either a project ID or a project number input.
type GKEPolicyControlProjectIdentity struct {
	Input         string
	ProjectID     string
	ProjectNumber string
}

// GKEPolicyControllerStatus is the normalized Policy Controller feature state
// cluster-agent needs to decide whether the enable workflow is complete.
type GKEPolicyControllerStatus struct {
	FeaturePath            string
	DesiredEnabled         bool
	MutationEnabled        bool
	LifecycleState         string
	MutationComponentState string
}

// gkePolicyControlTarget is the normalized request identity for the GKE cluster
// and Fleet project that Policy Controller should be enabled on.
type gkePolicyControlTarget struct {
	ProjectID      string
	Location       string
	ClusterName    string
	FleetProjectID string
	MembershipID   string
}

type AgentService struct {
	apiv1.UnimplementedClusterAgentServiceServer

	dataplaneCli     client.Client
	clusterNamespace string

	nodeRestarter NodeRestartProvider
	podRestarter  PodRestartProvider

	// provider "aws", "gcp"
	provider string

	gkePolicyControlClient GKEPolicyControlClient
	gkePolicyControlTarget *gkePolicyControlTarget
	tidbOperatorVersion    string
}

type Option func(*AgentService)

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

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

func WithTiDBOperatorVersion(version string) Option {
	return func(as *AgentService) {
		as.tidbOperatorVersion = version
	}
}

func WithClusterNamespace(namespace string) Option {
	return func(as *AgentService) {
		as.clusterNamespace = namespace
	}
}

func WithGKEPolicyControlClient(cli GKEPolicyControlClient) Option {
	return func(as *AgentService) {
		as.gkePolicyControlClient = cli
	}
}

func WithGKEPolicyControlTarget(projectID, location, clusterName, fleetProjectID string) Option {
	return func(as *AgentService) {
		projectID = strings.TrimSpace(projectID)
		location = strings.TrimSpace(location)
		clusterName = strings.TrimSpace(clusterName)
		fleetProjectID = strings.TrimSpace(fleetProjectID)
		if projectID == "" && location == "" && clusterName == "" && fleetProjectID == "" {
			as.gkePolicyControlTarget = nil
			return
		}
		as.gkePolicyControlTarget = &gkePolicyControlTarget{
			ProjectID:      projectID,
			Location:       location,
			ClusterName:    clusterName,
			FleetProjectID: fleetProjectID,
		}
	}
}

func NewAgentService(cli client.Client, opts ...Option) *AgentService {
	as := &AgentService{
		dataplaneCli: cli,
	}
	for _, opt := range opts {
		opt(as)
	}
	as.podRestarter = NewPodRestarter(as.tidbOperatorVersion)
	return as
}

func (s *AgentService) UseTiDBOperatorV2() bool {
	return s.tidbOperatorVersion == "v2"
}
