//go:generate ${GOBIN}/mockgen -destination service_mock.go -package=api ${GO_MODULE}/pkg/service/premiumupgrade/api Service
package api

import (
	"context"

	apprent "github.com/tidbcloud/devops/entity/approval"
	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	prem "github.com/tidbcloud/devops/entity/premiumupgrade"
)

type ComponentSelector struct {
	ClusterType ccent.ClusterType `json:"cluster_type"`
	Component   string            `json:"component"`
}

// ComponentImageTag pins the concrete image tag used for one component on all
// matching targets in a plan. ClusterType is part of the identity because the
// same component name can exist in both a Shared Pool and a Logical Cluster.
type ComponentImageTag struct {
	ClusterType ccent.ClusterType `json:"cluster_type"`
	Component   string            `json:"component"`
	ImageTag    string            `json:"image_tag"`
}

type ComponentPolicy struct {
	ClusterType   ccent.ClusterType          `json:"cluster_type"`
	Component     string                     `json:"component"`
	ApplyGroup    string                     `json:"apply_group"`
	AtomicGroup   bool                       `json:"atomic_group"`
	Priority      int                        `json:"priority"`
	ApplyStrategy ccent.ParameterApplyPolicy `json:"apply_strategy"`
	OrderRevision string                     `json:"order_revision"`
}

type CheckTargetConflictsParam struct {
	ClusterIDs []string `json:"cluster_ids"`
}

type TargetConflict struct {
	ClusterType ccent.ClusterType `json:"cluster_type"`
	ClusterID   string            `json:"cluster_id"`
	PlanID      uint64            `json:"plan_id"`
	PlanName    string            `json:"plan_name"`
	PlanStatus  prem.PlanStatus   `json:"plan_status"`
}

type UpgradeScope struct {
	Mode       prem.ScopeMode      `json:"mode"`
	Components []ComponentSelector `json:"components,omitempty"`
}

type PreviewPlanParam struct {
	ClusterIDs         []string            `json:"cluster_ids"`
	DesiredVersion     string              `json:"desired_version"`
	ExecutionMode      prem.ExecutionMode  `json:"execution_mode"`
	Scope              UpgradeScope        `json:"scope"`
	ComponentImageTags []ComponentImageTag `json:"component_image_tags,omitempty"`
}

type ApprovalRoute struct {
	Name          string                `json:"name"`
	Group         string                `json:"group"`
	Components    []string              `json:"components"`
	Repositories  []string              `json:"repositories,omitempty"`
	Approver      apprent.Approver      `json:"approver"`
	Method        apprent.ApproveMethod `json:"method"`
	SkipWhenEmpty bool                  `json:"skip_when_empty,omitempty"`
	Condition     string                `json:"condition,omitempty"`
}

type PreviewStepComponent struct {
	Component      string `json:"component"`
	BaseImage      string `json:"base_image"`
	FromVersion    string `json:"from_version"`
	ToVersion      string `json:"to_version"`
	FromImageTag   string `json:"from_image_tag,omitempty"`
	ToImageTag     string `json:"to_image_tag,omitempty"`
	ImageTagSource string `json:"image_tag_source,omitempty"`
	Noop           bool   `json:"noop"`
}

type PreviewStep struct {
	Sequence      uint32                     `json:"sequence"`
	ClusterType   ccent.ClusterType          `json:"cluster_type"`
	ClusterID     string                     `json:"cluster_id"`
	ApplyGroup    string                     `json:"apply_group"`
	Components    []PreviewStepComponent     `json:"components"`
	ApplyStrategy ccent.ParameterApplyPolicy `json:"apply_strategy"`
	Noop          bool                       `json:"noop"`
}

type PreviewTarget struct {
	ClusterType      ccent.ClusterType `json:"cluster_type"`
	ClusterID        string            `json:"cluster_id"`
	ClusterName      string            `json:"cluster_name"`
	SourceClusterIDs []string          `json:"source_cluster_ids"`
	TenantIDs        []string          `json:"tenant_ids"`
	SharedPoolID     string            `json:"shared_pool_id,omitempty"`
	LogicalRegion    string            `json:"logical_region,omitempty"`
	CloudProvider    string            `json:"cloud_provider,omitempty"`
	Steps            []*PreviewStep    `json:"steps"`
}

type PreviewPlanResult struct {
	ClusterIDs             []string            `json:"cluster_ids"`
	DesiredVersion         string              `json:"desired_version"`
	ExecutionMode          prem.ExecutionMode  `json:"execution_mode"`
	Scope                  UpgradeScope        `json:"scope"`
	ComponentImageTags     []ComponentImageTag `json:"component_image_tags,omitempty"`
	Targets                []*PreviewTarget    `json:"targets"`
	ApprovalRoutes         []ApprovalRoute     `json:"approval_routes"`
	Warnings               []string            `json:"warnings,omitempty"`
	ManifestHash           string              `json:"manifest_hash"`
	ComponentOrderRevision string              `json:"component_order_revision"`
}

type CreatePlanParam struct {
	PreviewPlanParam
	Name                 string `json:"name"`
	Reason               string `json:"reason"`
	Applicant            string `json:"applicant"`
	OperatorEntity       string `json:"operator_entity"`
	ExpectedManifestHash string `json:"expected_manifest_hash"`
	IdempotencyKey       string `json:"idempotency_key"`
}

type ListPlansParam struct {
	Applicant string
	TenantID  *uint64
	Statuses  []prem.PlanStatus
	Page      int
	PerPage   int
}

type Service interface {
	ListComponentPolicies(ctx context.Context) []ComponentPolicy
	CheckTargetConflicts(ctx context.Context, param CheckTargetConflictsParam) ([]TargetConflict, error)
	PreviewPlan(ctx context.Context, param PreviewPlanParam) (*PreviewPlanResult, error)
	CreatePlan(ctx context.Context, param CreatePlanParam) (*prem.UpgradePlan, error)
	GetPlan(ctx context.Context, id uint64) (*prem.UpgradePlan, error)
	ListPlans(ctx context.Context, param ListPlansParam) ([]*prem.UpgradePlan, int64, error)
	ApplyPlan(ctx context.Context, id uint64, operator string) error
	ApplyStep(ctx context.Context, planID, stepID uint64, operator string) error
	CancelPlan(ctx context.Context, id uint64, operator string) error
	RetryStep(ctx context.Context, planID, stepID uint64, operator string) error
}
