package premiumupgrade

import (
	"encoding/json"
	"slices"
	"time"

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

type (
	ScopeMode     string
	ExecutionMode string
	PlanStatus    string
	TargetStatus  string
	StepStatus    string
)

const (
	ScopeModeSelected   ScopeMode = "selected"
	ScopeModeAllPresent ScopeMode = "all_present"

	ExecutionModeOrchestrated ExecutionMode = "orchestrated"
	ExecutionModeStepByStep   ExecutionMode = "step_by_step"

	PlanStatusApprovalPendingCreate     PlanStatus = "approval_pending_create"
	PlanStatusApprovalCreating          PlanStatus = "approval_creating"
	PlanStatusApprovalReconcileRequired PlanStatus = "approval_reconcile_required"
	PlanStatusPendingApproval           PlanStatus = "pending_approval"
	PlanStatusApproved                  PlanStatus = "approved"
	PlanStatusRunning                   PlanStatus = "running"
	PlanStatusCancelling                PlanStatus = "cancelling"
	PlanStatusCompleted                 PlanStatus = "completed"
	PlanStatusPartialFailed             PlanStatus = "partial_failed"
	PlanStatusFailed                    PlanStatus = "failed"
	PlanStatusRejected                  PlanStatus = "rejected"
	PlanStatusCancelled                 PlanStatus = "cancelled"

	TargetStatusPending         TargetStatus = "pending"
	TargetStatusRunning         TargetStatus = "running"
	TargetStatusWaitingConflict TargetStatus = "waiting_conflict"
	TargetStatusCompleted       TargetStatus = "completed"
	TargetStatusFailed          TargetStatus = "failed"
	TargetStatusCancelled       TargetStatus = "cancelled"

	StepStatusPending            StepStatus = "pending"
	StepStatusPreparing          StepStatus = "preparing"
	StepStatusWaitingConflict    StepStatus = "waiting_conflict"
	StepStatusWaitingManualApply StepStatus = "waiting_manual_apply"
	StepStatusApplying           StepStatus = "applying"
	StepStatusSucceeded          StepStatus = "succeeded"
	StepStatusFailed             StepStatus = "failed"
	StepStatusSkipped            StepStatus = "skipped"
	StepStatusCancelled          StepStatus = "cancelled"
)

var (
	ScopeModeValues     = []ScopeMode{ScopeModeSelected, ScopeModeAllPresent}
	ExecutionModeValues = []ExecutionMode{ExecutionModeOrchestrated, ExecutionModeStepByStep}
	PlanStatusValues    = []PlanStatus{
		PlanStatusApprovalPendingCreate, PlanStatusApprovalCreating,
		PlanStatusApprovalReconcileRequired, PlanStatusPendingApproval,
		PlanStatusApproved, PlanStatusRunning, PlanStatusCancelling, PlanStatusCompleted,
		PlanStatusPartialFailed, PlanStatusFailed, PlanStatusRejected,
		PlanStatusCancelled,
	}
	TargetStatusValues = []TargetStatus{
		TargetStatusPending, TargetStatusRunning, TargetStatusWaitingConflict,
		TargetStatusCompleted, TargetStatusFailed, TargetStatusCancelled,
	}
	StepStatusValues = []StepStatus{
		StepStatusPending, StepStatusPreparing, StepStatusWaitingConflict, StepStatusWaitingManualApply,
		StepStatusApplying, StepStatusSucceeded, StepStatusFailed,
		StepStatusSkipped, StepStatusCancelled,
	}
)

func IsPlanTerminal(status PlanStatus) bool {
	return slices.Contains([]PlanStatus{
		PlanStatusCompleted,
		PlanStatusPartialFailed,
		PlanStatusFailed,
		PlanStatusRejected,
		PlanStatusCancelled,
	}, status)
}

func IsStepTerminal(status StepStatus) bool {
	return slices.Contains([]StepStatus{
		StepStatusSucceeded,
		StepStatusFailed,
		StepStatusSkipped,
		StepStatusCancelled,
	}, status)
}

type UpgradePlan struct {
	ID                     uint64                  `json:"id"`
	Name                   string                  `json:"name"`
	Reason                 string                  `json:"reason"`
	Applicant              string                  `json:"applicant"`
	OperatorEntity         string                  `json:"operator_entity"`
	DesiredVersion         string                  `json:"desired_version"`
	ExecutionMode          ExecutionMode           `json:"execution_mode"`
	ScopeMode              ScopeMode               `json:"scope_mode"`
	RequestedClusterIDs    []string                `json:"requested_cluster_ids"`
	RequestedComponents    []string                `json:"requested_components"`
	ApprovalManifest       json.RawMessage         `json:"approval_manifest"`
	ManifestHash           string                  `json:"manifest_hash"`
	ApprovalID             *uint64                 `json:"approval_id,omitempty"`
	ApprovalStatus         *apprent.ApprovalStatus `json:"approval_status,omitempty"`
	Status                 PlanStatus              `json:"status"`
	LastError              string                  `json:"last_error,omitempty"`
	MaxParallelTargets     uint32                  `json:"max_parallel_targets"`
	ComponentOrderRevision string                  `json:"component_order_revision"`
	IdempotencyKey         string                  `json:"idempotency_key"`
	CancelRequestedAt      *time.Time              `json:"cancel_requested_at,omitempty"`
	StartedAt              *time.Time              `json:"started_at,omitempty"`
	FinishedAt             *time.Time              `json:"finished_at,omitempty"`
	Version                uint64                  `json:"version"`
	CreatedAt              time.Time               `json:"created_at"`
	UpdatedAt              *time.Time              `json:"updated_at,omitempty"`
	Targets                []*UpgradeTarget        `json:"targets,omitempty"`
}

type UpgradeTarget struct {
	ID               uint64            `json:"id"`
	PlanID           uint64            `json:"plan_id"`
	ClusterType      ccent.ClusterType `json:"cluster_type"`
	ClusterID        string            `json:"cluster_id"`
	SourceClusterIDs []string          `json:"source_cluster_ids"`
	ClusterName      string            `json:"cluster_name"`
	TenantIDs        []string          `json:"tenant_ids"`
	SharedPoolID     string            `json:"shared_pool_id,omitempty"`
	LogicalRegion    string            `json:"logical_region,omitempty"`
	CloudProvider    string            `json:"cloud_provider,omitempty"`
	Status           TargetStatus      `json:"status"`
	LastError        string            `json:"last_error,omitempty"`
	StartedAt        *time.Time        `json:"started_at,omitempty"`
	FinishedAt       *time.Time        `json:"finished_at,omitempty"`
	CreatedAt        time.Time         `json:"created_at"`
	UpdatedAt        *time.Time        `json:"updated_at,omitempty"`
	Steps            []*UpgradeStep    `json:"steps,omitempty"`
}

type UpgradeStep struct {
	ID                    uint64                 `json:"id"`
	PlanID                uint64                 `json:"plan_id"`
	TargetID              uint64                 `json:"target_id"`
	Sequence              uint32                 `json:"sequence"`
	ApplyGroup            string                 `json:"apply_group"`
	Components            []UpgradeStepComponent `json:"components"`
	ApplyStrategy         string                 `json:"apply_strategy"`
	ApplyMode             json.RawMessage        `json:"apply_mode"`
	ConfigurationChangeID *uint64                `json:"configuration_change_id,omitempty"`
	Status                StepStatus             `json:"status"`
	StatusReason          string                 `json:"status_reason,omitempty"`
	AttemptCount          uint32                 `json:"attempt_count"`
	NextRetryAt           *time.Time             `json:"next_retry_at,omitempty"`
	ApplyDeadlineAt       *time.Time             `json:"apply_deadline_at,omitempty"`
	LastErrorCode         string                 `json:"last_error_code,omitempty"`
	LastErrorMessage      string                 `json:"last_error_message,omitempty"`
	Version               uint64                 `json:"version"`
	StartedAt             *time.Time             `json:"started_at,omitempty"`
	FinishedAt            *time.Time             `json:"finished_at,omitempty"`
	CreatedAt             time.Time              `json:"created_at"`
	UpdatedAt             *time.Time             `json:"updated_at,omitempty"`
}

type UpgradeStepComponent struct {
	Component    string `json:"component"`
	FromVersion  string `json:"from_version"`
	ToVersion    string `json:"to_version"`
	FromImageTag string `json:"from_image_tag,omitempty"`
	ToImageTag   string `json:"to_image_tag,omitempty"`
}

func (s *UpgradeStep) ComponentNames() []string {
	result := make([]string, 0, len(s.Components))
	for _, component := range s.Components {
		result = append(result, component.Component)
	}
	return result
}
