package event

import "time"

type Topic string

// TopicClusterEvent is the topic for cluster event, TopicMaintenanceWindowEvent is the topic for maintenance window event
// both internal pub sub and event bus, cluster event will be published to the topic named TopicClusterEvent
// both internal pub sub and event bus, maintenance window event will be published to the topic named TopicMaintenanceWindowEvent
const (
	TopicClusterEvent           Topic = "ClusterEvent"
	TopicMaintenanceWindowEvent Topic = "MaintenanceWindowEvent"
	TopicProjectMeteringData    Topic = "ProjectMeteringData"
)

var (
	Topics = []Topic{TopicClusterEvent, TopicMaintenanceWindowEvent}
)

func (t Topic) String() string {
	return string(t)
}

type Meta struct {
	Topic       string
	UUID        string
	TimestampMs int64
}

type (
	ClusterEventType           string
	ComponentType              string
	MaintenanceWindowEventType string
)

type Event interface {
	Event() // dummy function
}

// Note 1. notify clinic service team if you add or remove some events
// for more detail: refer to: https://pingcap.feishu.cn/wiki/wikcnJAZ642PH32aQzL8PKjtmge
const (
	ClusterEventTypeCreating     ClusterEventType = "ClusterCreating" // cluster is creating and created into metadb
	ClusterEventTypeCreated      ClusterEventType = "ClusterCreated"  // cluster is created and began to serve
	ClusterEventTypeInitializing ClusterEventType = "ClusterInitializing"
	ClusterEventTypeInitialized  ClusterEventType = "ClusterInitialized"
	ClusterEventTypePreparing    ClusterEventType = "ClusterPreparing"
	ClusterEventTypePrepared     ClusterEventType = "ClusterPrepared"
	ClusterEventTypeRestoring    ClusterEventType = "ClusterRestoring"
	ClusterEventTypeRestored     ClusterEventType = "ClusterRestored"
	ClusterEventTypeLaunching    ClusterEventType = "ClusterLaunching"
	ClusterEventTypeLaunched     ClusterEventType = "ClusterLaunched"

	// ClusterEventTypeNormal represents the cluster return to or become serving status from some status, such as creating, scaling, resuming
	ClusterEventTypeNormal ClusterEventType = "ClusterNormal"
	// ClusterEventTypeConfiguring represents that the cluster is undergoing configuration changes, such as password updates and traffic filter modifications.
	// NOTE: currently, this event is internal use only, don't use this in your code.
	ClusterEventTypeConfiguring ClusterEventType = "ClusterConfiguring"

	ClusterEventTypePausing                 ClusterEventType = "ClusterPausing"
	ClusterEventTypePaused                  ClusterEventType = "ClusterPaused"
	ClusterEventTypeResuming                ClusterEventType = "ClusterResuming"
	ClusterEventTypeResumed                 ClusterEventType = "ClusterResumed"
	ClusterEventTypeScaling                 ClusterEventType = "ClusterScaling"
	ClusterEventTypeScaled                  ClusterEventType = "ClusterScaled"
	ClusterEventTypeDeleting                ClusterEventType = "ClusterDeleting"
	ClusterEventTypeDeleted                 ClusterEventType = "ClusterDeleted"
	ClusterEventTypeCleared                 ClusterEventType = "ClusterCleared"
	ClusterEventTypeVolumeSnapshotRestoring ClusterEventType = "ClusterVolumeSnapshotRestoring"
	ClusterEventTypeVolumeSnapshotRestored  ClusterEventType = "ClusterVolumeSnapshotRestored"

	ComponentTypePd      ComponentType = "pd"
	ComponentTypeTidb    ComponentType = "tidb"
	ComponentTypeTikv    ComponentType = "tikv"
	ComponentTypeTiflash ComponentType = "tiflash"
)

type ClusterComponent struct {
	ComponentType ComponentType `json:"component_type"`
	TierName      string        `json:"tier_name"`     // the name of tier used by this component, for example: "8C16G"
	TierKey       string        `json:"tier_key"`      // the key of tier, for example: '2C4G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIKV'
	TierUnitKey   string        `json:"tier_unit_key"` // the key of tier unit, for example: 'aws:tikv:t4g.medium:gp3:4000:400'
	Quantity      int           `json:"quantity"`      // the node quantity of this component
	StorageSizeGi int64         `json:"storage_size_gi"`
}

type ClusterTopology struct {
	Components  map[ComponentType]*ClusterComponent `json:"components"`
	AccessPoint []*AccessPoint                      `json:"access_points,omitempty"`
}

type AccessPoint struct {
	ID               uint64 `json:"accesspoint_id"`
	TidbNodeQuantity uint32 `json:"tidb_node_quantity"`
}

type ClusterEventExtraInfo struct {
	DurationSeconds *int64 `json:"duration_seconds,string,omitempty"`

	// These fields below are only available for ClusterScaling event now.
	TopologyBefore *ClusterTopology `json:"topology_before,omitempty"`
	TopologyAfter  *ClusterTopology `json:"topology_after,omitempty"`
}

type ClusterEvent struct {
	TenantID  uint64 `json:"tenant_id,string"`
	ProjectID uint64 `json:"project_id,string"`
	ClusterID uint64 `json:"cluster_id,string"`

	// The operator's userId who did an operation which result in this event.
	// It may be zero that presents the operator is unknown or this event without operator.
	OperatorID uint64 `json:"operator_id,string"`

	// id of operation, it is used to associate with cluster_operation record.
	// It may be zero that presents this event without any cluster operation.
	// NOTE 1: Due to some technological problem, this field always is 0 in xxx-ing event.
	OperationID uint64 `json:"operation_id,string"`

	IsDevTier        bool                   `json:"is_dev_tier"`
	Status           string                 `json:"status"` // refer to idl/core/core/core.proto:ClusterStatus
	EventType        ClusterEventType       `json:"event_type"`
	EventCreatedAtMs int64                  `json:"event_created_at_ms,string"` // unix timestamp in milliseconds t.UnixMilli()
	ExtraInfo        *ClusterEventExtraInfo `json:"extra_info,omitempty"`
}

var _ Event = &ClusterEvent{}

func (*ClusterEvent) Event() {}

type ClusterThresholdStateChanged struct {
	TenantID    uint64
	ProjectID   uint64
	ClusterID   uint64
	TriggerTime time.Time

	IsDevTier bool

	IngressThrottled bool
	EgressThrottled  bool
}

func (ev *ClusterThresholdStateChanged) AnyThrottled() bool {
	return ev.IngressThrottled || ev.EgressThrottled
}

type MaintenanceTask struct {
	ID                 uint64 `json:"id,string"`
	Status             string `json:"status"`                      // Refer to idl/core/core/core.proto:MaintenanceTask.Status
	ScheduledApplyTime int64  `json:"scheduled_apply_time,string"` // time.Unix()
}

type MaintenanceTaskExecution struct {
	ID                uint64 `json:"id,string"`
	MaintenanceTaskID uint64 `json:"maintenance_task_id,string"`
	ClusterID         uint64 `json:"cluster_id,string"`
	Status            string `json:"status"` // Refer to idl/core/core/core.proto:MaintenanceTaskExecution.Status
}

type MaintenanceWindowEvent struct {
	ProjectID                uint64                     `json:"project_id,string"`
	EventType                MaintenanceWindowEventType `json:"event_type"`
	EventCreatedAtMs         int64                      `json:"event_created_at_ms,string"` // unix timestamp in milliseconds t.UnixMilli()
	MaintenanceTask          *MaintenanceTask           `json:"maintenance_task,omitempty"`
	MaintenanceTaskExecution *MaintenanceTaskExecution  `json:"maintenance_task_execution,omitempty"`
}

const (
	MaintenanceWindowEventTypeTaskRunning       MaintenanceWindowEventType = "TaskRunning"
	MaintenanceWindowEventTypeExecutionRunning  MaintenanceWindowEventType = "ExecutionRunning"
	MaintenanceWindowEventTypeExecutionDone     MaintenanceWindowEventType = "ExecutionDone"
	MaintenanceWindowEventTypeExecutionCanceled MaintenanceWindowEventType = "ExecutionCanceled"
)

var _ Event = &MaintenanceWindowEvent{}

func (*MaintenanceWindowEvent) Event() {}

// ProjectMeteringData represents a pre-aggregated metering data for one project in a specific time span.
// This struct is internal use ONLY, please don't refer it in anywhere else.
// The producer for it is `provider-cost` service, and the consumer is `core-svc-worker`.
type ProjectMeteringData struct {
	ProjectID uint64                    `json:"project_id,string"`
	StartTime int64                     `json:"start_time,string"`
	EndTime   int64                     `json:"end_time,string"`
	BatchID   string                    `json:"batch_id"`
	Items     []ProjectMeteringDataItem `json:"items"`
}

type ProjectMeteringDataItem struct {
	Amount          float64           `json:"amount"`
	Cost            float64           `json:"cost"`
	Labels          map[string]string `json:"labels"`
	ServiceName     string            `json:"service_name"`
	ResourceType    string            `json:"resource_type"`
	ResourceSubType string            `json:"resource_subtype"`
	Unit            string            `json:"unit"`
	DataVersion     string            `json:"data_version"`
}
