package domain

import (
	"time"

	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/dataflow-service/idl/pbgen/proto/dataflow"
	bizerr_wrapper "github.com/tidbcloud/dataflow-service/pkg/bizerr-wrapper"
	"github.com/tidbcloud/pkgv2/bizerr"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type RestoreMode string

const (
	RestoreModeSnapshot RestoreMode = "snapshot"
	RestoreModePITR     RestoreMode = "pitr"
)

type CreateRestoreReqV2 struct {
	// target tenant id. It must be same as backup's tenant id because we don't support cross tenant restore
	TenantID uint64
	// target project id. May be different from backup's project id, e.g. cross project restore
	ProjectID uint64
	Mode      RestoreMode
	BackupID  uint64 // required
	// [optional] You can specify backup region that you want to restore from. It's useful when the backup has enabled dual-region.
	// If not specified, will try to restore from the bucket covering the target cluster's region; falls back to the backup's primary bucket if no match.
	BackupRegion  string
	ClusterConfig ClusterConfig // target cluster config
	UserID        uint64

	// just for pitr
	RestoredTs int64
	ClusterID  uint64 // the logbackup's cluster id, required for pitr restore.

	// infra
	BRVersion       string // br tool version. optional. e.g. "v7.5.6"
	BRImageTag      string // br tool image tag. optional. e.g. "testing-v9.0.0-beta-2-hotfix-8960-dev"
	BRCustomOptions string // e.g. "--with-sys-table, --check-requirements=false..."
}

func (req *CreateRestoreReqV2) Validate() bizerr.BizError {
	if req.TenantID == 0 {
		return bizerr_wrapper.NewInvalidInputError("tenantID is required")
	}
	if req.ProjectID == 0 {
		return bizerr_wrapper.NewInvalidInputError("projectID is required")
	}
	if req.Mode != RestoreModeSnapshot && req.Mode != RestoreModePITR {
		return bizerr_wrapper.NewInvalidInputError("mode must be 'snapshot' or 'pitr'")
	}
	if req.ClusterConfig.Region == "" {
		return bizerr_wrapper.NewInvalidInputError("clusterConfig.region is required")
	}
	if req.ClusterConfig.Name == "" {
		return bizerr_wrapper.NewInvalidInputError("clusterConfig.name is required")
	}
	switch req.Mode {
	case RestoreModeSnapshot:
		// PiTR select backup by restoredTs, so backupID is not required. But for snapshot restore, backupID is required to identify which backup to restore.
		if req.BackupID == 0 {
			return bizerr_wrapper.NewInvalidInputError("backupID is required")
		}
	case RestoreModePITR:
		if req.ClusterID == 0 {
			return bizerr_wrapper.NewInvalidInputError("clusterID is required for pitr restore")
		}
		if req.RestoredTs == 0 {
			return bizerr_wrapper.NewInvalidInputError("restoredTs is required for pitr restore")
		}
	}
	return nil
}

type CreateRestoreRespV2 struct {
	ID         uint64
	ClusterID  uint64
	WorkflowID string
}

type CancelRestoreReqV2 struct {
	RestoreID uint64
}

type CancelRestoreRespV2 struct {
	WorkflowID string
}

type ClusterConfig struct {
	// if specify cluster id, means cluster is exist, no need to create
	ClusterID    uint64             `json:"cluster_id,omitempty"`     // optional, if not specified, create a new cluster
	Region       string             `json:"region,omitempty"`         // required
	Name         string             `json:"name,omitempty"`           // required.
	RootPassword string             `json:"root_password,omitempty"`  // optional, only for create new cluster
	Port         int32              `json:"port,omitempty"`           // optional, only for create new cluster
	Components   *ClusterComponents `json:"components,omitempty"`     // optional, only for create new cluster
	IpAccessList []IpAccessListItem `json:"ip_access_list,omitempty"` // optional, only for create new cluster

	// optional, only for cluster existed. Clone all information
	Cloned bool `json:"cloned,omitempty"`
}

type ClusterComponents struct {
	TiProxy *TiProxyComponent `json:"tiproxy,omitempty"`
	Tidb    *TiDBComponent    `json:"tidb,omitempty"`
	Tikv    *TiKVComponent    `json:"tikv,omitempty"`
	Tiflash *TiFlashComponent `json:"tiflash,omitempty"`
}

type TiProxyComponent struct {
	Compute
	NodeSize     string `json:"node_size,omitempty"`
	NodeQuantity int32  `json:"node_quantity,omitempty"`
}

type TiDBComponent struct {
	Compute
	NodeSize     string `json:"node_size,omitempty"`
	NodeQuantity int32  `json:"node_quantity,omitempty"`
}

type TiKVComponent struct {
	Compute
	NodeSize       string                                      `json:"node_size,omitempty"`
	StorageType    dataflow.SimpleClusterComponent_StorageType `json:"storage_type,omitempty"`
	DataStoreIops  int32                                       `json:"data_store_iops,omitempty"` // Only for TiKV DATA disk.
	RaftIops       int32                                       `json:"raft_iops,omitempty"`       // Only for tikv.
	StorageSizeGib int64                                       `json:"storage_size_gib,omitempty"`
	NodeQuantity   int32                                       `json:"node_quantity,omitempty"`
}

type TiFlashComponent struct {
	Compute
	NodeSize       string                                      `json:"node_size,omitempty"`
	StorageType    dataflow.SimpleClusterComponent_StorageType `json:"storage_type,omitempty"`
	StorageSizeGib int64                                       `json:"storage_size_gib,omitempty"`
	NodeQuantity   int32                                       `json:"node_quantity,omitempty"`
}

type Compute struct {
	CpuNum    int32 `json:"cpu_num,omitempty"`
	MemoryGib int64 `json:"memory_gib,omitempty"`
}

type IpAccessListItem struct {
	Cidr        string `protobuf:"bytes,1,opt,name=cidr,proto3" json:"cidr,omitempty"`
	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
}

type ListRestoreFilter struct {
	TenantIDs        []uint64
	ProjectIDs       []uint64
	ClusterIDs       []uint64
	RestoreIDs       []uint64
	BackupClusterIDs []uint64
}

type ListRestoreReqV2 struct {
	Filter *ListRestoreFilter
	Pager  *Pager
}

type ListRestoreRespV2 struct {
	Items []ListRestoreRespV2Item
	Total int64
}

type ListRestoreRespV2Item struct {
	Id                   uint64
	CreateTime           time.Time
	FinishedTime         *time.Time
	Mode                 RestoreMode
	Status               RestoreStatusEnum
	BackupId             uint64
	BackupName           string
	BackupClusterId      uint64
	RestoreClusterName   string
	RestoreClusterID     uint64
	RestoreClusterRegion string
	Progress             float64
	RestoreTS            *int64
	RestoreTSO           string
}

type RestoreStatusEnum string

const (
	RestoreStatusEnumPending    RestoreStatusEnum = "pending"
	RestoreStatusEnumRunning    RestoreStatusEnum = "running"
	RestoreStatusEnumFailed     RestoreStatusEnum = "failed"
	RestoreStatusEnumSuccess    RestoreStatusEnum = "success"
	RestoreStatusEnumTerminated RestoreStatusEnum = "terminated"
)

type QueryRestoreReqV2 struct {
	Filter *ListRestoreFilter
}

type QueryRestoreRespV2 struct {
	Id               uint64
	CreateTime       time.Time
	Status           RestoreStatusEnum
	BackupId         uint64
	BackupClusterId  uint64
	RestoreClusterID uint64
	Progress         float64
	Steps            []RestoreStep
}

type RestoreStep struct {
	Progress float64
	StepName string
}

type RestoreToClusterType string

const (
	RestoreToClusterTypeNew   RestoreToClusterType = "new"
	RestoreToClusterTypeExist RestoreToClusterType = "exist"
	RestoreToClusterTypeClone RestoreToClusterType = "clone"
)

type PiTRTimeRange struct {
	LogBackup   entity.LogBackup
	LogBackupID entity.ID
	BucketID    entity.ID
	StartTS     int64
	EndTS       int64
}

type PiTRRegionTimeRange struct {
	Region     *RegionInfo
	TimeRanges []*PiTRTimestampRange
}

type PiTRTimestampRange struct {
	StartTS *timestamppb.Timestamp
	EndTS   *timestamppb.Timestamp
}

type PiTRTimeRangeListReq struct {
	OrgId     uint64
	ProjectId uint64
	ClusterId uint64
}
