package repo

import (
	"context"

	"github.com/tidbcloud/cluster-service-ng/domain/entity"
)

// FeatureFlagsPatch is the per-field patch for the 6 autoscale flags
// on ru_configs — SPARSE representation for PATCH semantics: nil =
// "don't touch this field" (preserve current value); non-nil = overwrite.
//
// For the DENSE representation used by POST/SYNC REPLACE_ALL
// (where nil = "no override at this scope, fall through to server
// default at read time"), use *entity.RuConfigFeatureFlags directly.
//
// Scope of PatchFeatureFlags/ReplaceFeatureFlags: the 6 autoscale
// optional fields ONLY. The 2 plain-bool fields (EnableTici,
// EnableResourceManager) are preserved verbatim from the existing
// row on any policy write; the MySQL impl's copyFeatureFlags /
// mergeFeatureFlags carry them through.
type FeatureFlagsPatch struct {
	EnableScaleSharedPoolByRu           *bool
	EnableScaleTidbGroupByRu            *bool
	EnableAutoscaleTikvCpuScaleOut      *bool
	EnableAutoscaleDiskExpand           *bool
	EnableAutoscaleIopsThroughputExpand *bool
	EnableAutoscaleTiproxyScaleIn       *bool
}

// HasAnyChange returns true if at least one field is being modified.
// Useful for skip-if-noop optimization in write RPCs.
func (p *FeatureFlagsPatch) HasAnyChange() bool {
	if p == nil {
		return false
	}
	return p.EnableScaleSharedPoolByRu != nil ||
		p.EnableScaleTidbGroupByRu != nil ||
		p.EnableAutoscaleTikvCpuScaleOut != nil ||
		p.EnableAutoscaleDiskExpand != nil ||
		p.EnableAutoscaleIopsThroughputExpand != nil ||
		p.EnableAutoscaleTiproxyScaleIn != nil
}

type RuConfigRepo interface {
	SetRuConfig(ctx context.Context, id string, scope entity.RuConfigScope, ruConfig *entity.RuConfig) error
	GetRuConfig(ctx context.Context, id string, scope entity.RuConfigScope) (*entity.RuConfig, error)
	// GetRuConfigWithFound keeps GetRuConfig's fallback return value while
	// exposing whether the DB row actually exists.
	GetRuConfigWithFound(ctx context.Context, id string, scope entity.RuConfigScope) (*entity.RuConfig, bool, error)
	DeleteRuConfig(ctx context.Context, id string, scope entity.RuConfigScope) error

	// GetServerDefaultRuConfig returns the server-default RuConfig
	// (loaded from YAML at startup). Use for
	// `resolve.PriorityResolveForSharedPool(tenantRC, serverDefault)`
	// at CreateSharedPool time so tenant-nil autoscale flags fall
	// through to the server default. The 6 autoscale optional flags
	// are guaranteed non-nil on the returned FeatureFlags block
	// (YAML always has a value), so per-flag resolution is total.
	GetServerDefaultRuConfig(ctx context.Context) (*entity.RuConfig, error)

	// GetFeatureFlags returns the FeatureFlags block for the given
	// (scope, id) row, OR the default FeatureFlags if no row exists.
	// Returned struct is the raw row content (with optional *bool
	// pointers unset = "not present in this row").
	GetFeatureFlags(ctx context.Context, id string, scope entity.RuConfigScope) (*entity.RuConfigFeatureFlags, error)

	// PatchFeatureFlags: SPARSE PATCH semantics. nil pointer in the
	// patch = "don't touch this field" (current value preserved;
	// cannot distinguish "not in patch" from "explicitly clear" with
	// a single *bool per field — see ReplaceFeatureFlags for the
	// clear-capable counterpart); non-nil = "set to this value".
	// Idempotent: HasAnyChange()==false → row not touched. Row
	// created (with patched FeatureFlags + server-default elsewhere)
	// if absent.
	PatchFeatureFlags(ctx context.Context, id string, scope entity.RuConfigScope, patch FeatureFlagsPatch) error

	// ReplaceFeatureFlags: DENSE counterpart to PatchFeatureFlags for
	// POST CreateOrReplaceAutoscalePolicy and SYNC REPLACE_ALL. nil
	// pointer in input = "no override at this scope" (column becomes
	// NULL → falls through to server default at read time); non-nil =
	// "explicit override". nil flags = "wipe all overrides" (MVP
	// contract for POST `specs: []`). Row created if absent; other
	// (non-FeatureFlags) fields on existing row left untouched.
	ReplaceFeatureFlags(ctx context.Context, id string, scope entity.RuConfigScope, flags *entity.RuConfigFeatureFlags) error
}
