package vo

import (
	"fmt"
	"time"

	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/console"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/rpc"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type Backup struct {
	ID         string               `json:"id,omitempty"`
	Name       string               `json:"name,omitempty"`
	Type       string               `json:"type,omitempty"`
	CreatedAt  *time.Time           `json:"created_at,omitempty"`
	BackupTime *time.Time           `json:"backup_time,omitempty"`
	ExpireTime *time.Time           `json:"expire,omitempty"`
	ClusterID  string               `json:"cluster_id,omitempty"`
	State      constant.BackupState `json:"state,omitempty"`
}

type RecycledInstance struct {
	ClusterID                 string     `json:"cluster_id,omitempty"`
	DisplayName               string     `json:"display_name,omitempty"`
	ProjectID                 string     `json:"project_id,omitempty"`
	Region                    string     `json:"region,omitempty"`
	DeleteTime                *time.Time `json:"delete_time,omitempty"`
	EnhancedEncryptionEnabled bool       `json:"enhanced_encryption_enabled,omitempty"`
	ZoneRedundantEnabled      bool       `json:"zone_redundant_enabled,omitempty"`
	Provider                  string     `json:"provider,omitempty"`
}

type RestoreCluster struct {
	DisplayName          string
	SpendingLimitMonthly int32
	AutoScaling          *AutoScaling
	ZoneRedundantEnabled bool `json:"zone_redundant_enabled,omitempty"`
}

type AutoScaling struct {
	MinRcu uint32 `json:"min_rcu,omitempty"`
	MaxRcu uint32 `json:"max_rcu,omitempty"`
}

type Restore struct {
	TenantId       string               `json:"tenant_id,omitempty"`
	ProjectId      *string              `json:"project_id,omitempty"`
	BackupId       *string              `json:"backup_id,omitempty"`
	PointInTime    *PointInTime         `json:"point_in_time,omitempty"`
	RestoreType    constant.RestoreType `json:"restore_type,omitempty"`
	RestoreCluster *RestoreCluster      `json:"restore_cluster,omitempty"`
}

type PointInTime struct {
	ClusterId  string    `json:"cluster_id,omitempty"`
	BackupTime time.Time `json:"backup_time,omitempty"`
}

func (b *Backup) GenerateBackupName() {
	b.Name = fmt.Sprintf("backups/%s", b.ID)
}

// BackupFromPersisted builds a Backup view from a persisted UserBackup entity.
func BackupFromPersisted(backup *entity.UserBackup) *Backup {
	if backup == nil {
		return nil
	}
	view := &Backup{
		ID:         backup.ID,
		CreatedAt:  backup.CreatedAt,
		ClusterID:  backup.ClusterID,
		ExpireTime: backup.ExpireTime,
		BackupTime: backup.BackupTime,
		State:      backup.State,
		Type:       string(constant.BackupTypeAuto),
	}
	view.GenerateBackupName()
	return view
}

type ListUserBackupsOpt struct {
	ClusterId string
	TenantId  string
	PageSize  int32
	Offset    int64
	OrderBy   string
}

type ListRecycledInstancesOpt struct {
	TenantId   string
	PageSize   int32
	Offset     int64
	OrderBy    string
	RegionIds  []string
	ProjectIds []string
	StartTime  *time.Time
	EndTime    *time.Time
}

func (b *Backup) ConvertToConsole() *console.Backup {
	backup := &console.Backup{
		BackupId:   b.ID,
		Name:       b.Name,
		Type:       b.Type,
		CreateTime: timestamppb.New(*b.BackupTime),
		ClusterId:  b.ClusterID,
		ExpireTime: timestamppb.New(*b.ExpireTime),
		BackupTime: timestamppb.New(*b.BackupTime),
		State:      convertStateToConsole(b.State),
	}
	return backup
}

func (r *RecycledInstance) ConvertToConsole() *console.RecycledInstance {
	instance := &console.RecycledInstance{
		ClusterId:                 r.ClusterID,
		DisplayName:               r.DisplayName,
		ProjectId:                 r.ProjectID,
		Region:                    r.Region,
		EnhancedEncryptionEnabled: r.EnhancedEncryptionEnabled,
		ZoneRedundantEnabled:      r.ZoneRedundantEnabled,
		Provider:                  r.Provider,
	}
	if r.DeleteTime != nil {
		instance.DeleteTime = timestamppb.New(*r.DeleteTime)
	}
	return instance
}

func convertStateToConsole(state constant.BackupState) console.BackupStateType_Enum {
	switch state {
	case constant.BackupStateSucceeded:
		return console.BackupStateType_SUCCEEDED
	case constant.BackupStateFailed:
		return console.BackupStateType_FAILED
	default:
		return console.BackupStateType_UNSPECIFIED
	}
}

type ListRestoresOpt struct {
	ClusterId string
	TenantId  string
	PageSize  int32
	Offset    int64
	OrderBy   string
}

type RestoreJob struct {
	Id          string                   `json:"id,omitempty"`
	CreatedAt   time.Time                `json:"created_at,omitempty"`
	ProjectId   string                   `json:"project_id,omitempty"`
	Region      string                   `json:"region,omitempty"`
	TenantId    string                   `json:"tenant_id,omitempty"`
	ClusterId   string                   `json:"cluster_id,omitempty"`
	CreatedBy   string                   `json:"created_by,omitempty"`
	State       constant.RestoreJobState `json:"state,omitempty"`
	SizeBytes   uint64                   `json:"size_bytes,omitempty"`
	BackupId    *string                  `json:"backup_id,omitempty"`
	PointInTime *PointInTime             `json:"point_in_time,omitempty"`
	Error       string                   `json:"error,omitempty"`
	Step        string                   `json:"step"`
	Percent     int32                    `json:"percent"`
}

func (r *RestoreJob) GetConsoleState() console.RestoreState_Enum {
	switch r.State {
	case constant.RestoreJobStateRunning:
		return console.RestoreState_RUNNING
	case constant.RestoreJobStateFailed:
		return console.RestoreState_FAILED
	case constant.RestoreJobStateSucceeded:
		return console.RestoreState_SUCCEEDED
	case constant.RestoreJobSatePending:
		return console.RestoreState_PENDING
	default:
		return console.RestoreState_UNSPECIFIED
	}
}

type BackupSchedule struct {
	Id           string `json:"id,omitempty"`
	ClusterId    string `json:"cluster_id,omitempty"`
	ScheduleCron string `json:"schedule_cron,omitempty"`
	Retention    int    `json:"retention,omitempty"`
	// schedule time in format "HH:MM"
	ScheduleTime string `json:"schedule_time,omitempty"`
}

type ListBackupSchedulesOpt struct {
	ClusterId string
	OrgId     uint64
}

type UpdateBackupSchedulesOpt struct {
	Id            string
	ClusterId     string
	OrgId         uint64
	ScheduleTime  string
	RetentionDays int
}

func (b *BackupSchedule) ConvertToRPC() *rpc.BackupPolicy {
	policy := &rpc.BackupPolicy{
		Id:           b.Id,
		ClusterId:    b.ClusterId,
		Retention:    uint64(b.Retention),
		ScheduleTime: b.ScheduleTime,
	}
	return policy
}
