package server

import (
	"context"
	"time"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func (s *serverImpl) GetResolvedAutoscalePolicyBundle(
	ctx context.Context,
	sharedPoolID string,
	componentType entity.ComponentType,
) (*entity.ResolvedAutoscalePolicyBundle, error) {
	start := time.Now()
	// Direct read of HPA + ru_configs.FeatureFlags for the
	// shared pool. The autoscale_policies table is no longer a
	// SHARED_POOL storage point (storage-point exclusivity
	// invariant; see
	// docs/design/tenant-aware-autoscale-policy-redesign.md
	// §Storage-point exclusivity).
	bundle, err := s.getResolvedAutoscalePolicyBundleDirectRead(ctx, sharedPoolID, componentType)
	if err != nil {
		return nil, err
	}
	autoscalePolicyResolveDurationSeconds.WithLabelValues(componentType.String()).Observe(time.Since(start).Seconds())
	autoscalePolicyBundleVersion.WithLabelValues(apiv1.AutoscalePolicy_SHARED_POOL.String(), componentType.String()).Set(float64(bundle.GetVersion()))
	return bundle, nil
}

func (s *serverImpl) ApplyMaterializedAutoscalePolicies(
	_ context.Context,
	subjectType entity.AutoscalePolicySubjectType,
	_ string,
	_ []*entity.AutoscalePolicy,
) ([]*entity.AutoscalePolicy, error) {
	// ApplyMaterializedAutoscalePolicies is a deprecated RPC. The
	// `autoscale_policies` table is dormant for SHARED_POOL/CLUSTER
	// (storage-point exclusivity invariant; see
	// docs/design/tenant-aware-autoscale-policy-redesign.md
	// §Storage-point exclusivity). DEFAULT/TENANT scope was never
	// written to this table; that scope is managed exclusively
	// through the `autoscale_policy_bundle_specs` table via the policy
	// RPCs (`CreateOrReplaceAutoscalePolicy` /
	// `PatchAutoscalePolicy` / `SyncAutoscalePolicy`). The RPC is
	// preserved for wire compatibility only; every request is
	// rejected at the service boundary.
	switch subjectType {
	case apiv1.AutoscalePolicy_SHARED_POOL, apiv1.AutoscalePolicy_CLUSTER:
		return nil, status.Errorf(codes.FailedPrecondition,
			"autoscale_policies is no longer a SHARED_POOL/CLUSTER storage point — "+
				"write HPA `spec.behavior` (via SetHPAScaleBehavior) and "+
				"`ru_configs.FeatureFlags` (via SetRuConfig) instead. "+
				"See docs/design/tenant-aware-autoscale-policy-redesign.md §Storage-point exclusivity.")
	case apiv1.AutoscalePolicy_SUBJECT_TYPE_DEFAULT, apiv1.AutoscalePolicy_SUBJECT_TYPE_TENANT:
		return nil, status.Errorf(codes.Unimplemented,
			"DEFAULT/TENANT scope policies are managed exclusively through the "+
				"autoscale_policy_bundle_specs table — use CreateOrReplaceAutoscalePolicy / "+
				"PatchAutoscalePolicy / SyncAutoscalePolicy at the bundle RPC layer.")
	default:
		return nil, status.Errorf(codes.InvalidArgument, "invalid subject type %q", subjectType)
	}
}

func (s *serverImpl) EvaluateAutoscalePolicy(
	ctx context.Context,
	sharedPoolID string,
	componentType entity.ComponentType,
	direction entity.AutoscalePolicyDirection,
	capability entity.AutoscalePolicyCapability,
	trigger entity.AutoscalePolicyTrigger,
) (*entity.EvaluateAutoscalePolicyResponse, error) {
	start := time.Now()
	defer func() {
		autoscalePolicyResolveDurationSeconds.WithLabelValues(componentType.String()).Observe(time.Since(start).Seconds())
	}()

	// Regional Runtime Guard is bypass/store-only this iteration: it is
	// persisted via Upsert/Delete/List but does NOT influence any scale
	// decision here. See docs/design/tenant-aware-autoscale-policy-redesign.md
	// §Regional Runtime Guard (Part II no-goal: no runtime enforcement).
	// TODO: when runtime enforcement lands, re-introduce the scale-in guard
	// check here and surface the operator's guard.Reason in the response/log.
	//
	// Direct-read of HPA + ru_configs.FeatureFlags for the shared pool.
	// The autoscale_policies table is no longer a SHARED_POOL storage
	// point (storage-point exclusivity invariant; see the design doc
	// §Storage-point exclusivity).
	autoscalePolicyResolveSource.WithLabelValues("runtime_specs").Inc()
	return s.evaluateAutoscalePolicyDirectRead(ctx, sharedPoolID, componentType, direction, capability, trigger)
}

func (s *serverImpl) ListAutoscalePolicies(
	ctx context.Context,
	subjectType entity.AutoscalePolicySubjectType,
	subjectID string,
	componentType entity.ComponentType,
) (*entity.ListAutoscalePoliciesResponse, error) {
	// The autoscale_policies table is dormant for SHARED_POOL/CLUSTER
	// (storage-point exclusivity invariant; see
	// docs/design/tenant-aware-autoscale-policy-redesign.md
	// §Storage-point exclusivity). The RPC now rejects
	// SHARED_POOL/CLUSTER/UNSPECIFIED scope queries at the service
	// boundary and points the operator at the direct-read path
	// (`GetEffectiveAutoscalePolicy` for the resolved view, or
	// the underlying `readRuntimeSpecs` for the raw spec list).
	return listAutoscalePoliciesDirectReadRejected(subjectType, subjectID, componentType)
}
