package tenant

import (
	"time"

	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/devops/api"
	tntent "github.com/tidbcloud/devops/entity/tenant"
	"github.com/tidbcloud/devops/pkg/infra/clients/accountcli"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

type SortByType string
type OrderType string

const (
	SortByTypeCreatedAt SortByType = "created_at"
	SortByTypeTimezone  SortByType = "timezone"
	OrderTypeASC        OrderType  = "asc"
	OrderTypeDESC       OrderType  = "desc"
)

type ListTenantReq struct {
	api.PaginationReq `json:",inline"`
	ListTenantFilter  `json:",inline"`

	Search string     `form:"search" json:"search"`
	SortBy SortByType `form:"sort_by" json:"sort_by" binding:"omitempty,oneof=timezone created_at"`
	Order  OrderType  `form:"order" json:"order" binding:"omitempty,oneof=asc desc"`
}

type ListTenantFilter struct {
	Status       []string `form:"status" json:"status" enums:"pending,active,inactive,deleted,cleared" binding:"dive,oneof=pending active inactive deleted cleared"`
	Kind         []string `form:"kind" json:"kind"  binding:"dive,oneof=internal external" enums:"internal,external"`
	Plan         []string `form:"plan" json:"plan"  binding:"dive,oneof=new_user free_trial poc on_demand" enums:"new_user,free_trial,poc,on_demand"`
	Region       []string `form:"region" json:"region" binding:"dive,oneof=APAC EMEA JAPAN NA UNKNOWN RDG TEST_INFRA LATAM" enums:"APAC,EMEA,JAPAN,NA,UNKNOWN,RDG,TEST_INFRA,LATAM"`
	Segmentation []string `form:"segmentation" json:"segmentation" binding:"dive,oneof=SKA KA SMB" enums:"SKA,KA,SMB"`
}

type ListTenantResp struct {
	Total   int64            `json:"total"`
	Tenants []TenantResponse `json:"items"`
}

type GetTenantResp struct {
	TenantResponse `json:",inline"`
}

type CommonResp struct {
	api.BaseResp

	InstanceID *uint64 `json:"instance_id"` // the operation instance id
}

type UpdateTenantReq struct {
	Reason       string `json:"reason" binding:"required"`
	TenantID     uint64 `json:"tenant_id,string" binding:"required"`
	TenantEmail  string `json:"tenant_email"`
	TenantStatus string `json:"tenant_status"`
	TenantType   string `json:"tenant_type"`
}

type DeleteTenantReq struct {
	Reason string `json:"reason" binding:"required"`
}

type DeleteProjectReq struct {
	Reason    string `json:"reason" binding:"required"`
	TenantID  uint64 `json:"tenant_id,string" binding:"required"`
	ProjectID uint64 `json:"project_id,string" binding:"required"`
}

type DeleteAccountUserReq struct {
	Reason    string `json:"reason" binding:"required"`
	UserID    uint64 `json:"user_id,string" binding:"required"`
	UserEmail string `json:"user_email"`
}

type GetUsersReq struct {
	Search    string `form:"search"`
	Page      int64  `form:"page"`
	PageSize  int64  `form:"page_size"`
	FullEmail bool   `form:"full_email"`
}

type GetUsersResp struct {
	api.BaseResp

	Total int64              `json:"total"`
	Items []*accountcli.User `json:"items"`
}

type GetProjectResp struct {
	Project *accountcli.Project `json:"project"`
}

type AddUserToTenantReq struct {
	Reason    string `json:"reason" binding:"required"`
	TenantID  uint64 `json:"tenant_id,string" binding:"required"`
	UserID    uint64 `json:"user_id,string" binding:"required"`
	UserEmail string `json:"user_email"`
	UserRole  string `json:"user_role" binding:"required"`
}

type AddUserToProjectReq struct {
	Reason    string `json:"reason" binding:"required"`
	TenantID  uint64 `json:"tenant_id,string" binding:"required"`
	ProjectID uint64 `json:"project_id,string" binding:"required"`
	UserID    uint64 `json:"user_id,string" binding:"required"`
	UserEmail string `json:"user_email"`
	UserRole  string `json:"user_role" binding:"required"`
}

type RemoveUserFromTenantReq struct {
	Reason    string `json:"reason" binding:"required"`
	TenantID  uint64 `json:"tenant_id,string" binding:"required"`
	UserID    uint64 `json:"user_id,string" binding:"required"`
	UserEmail string `json:"user_email"`
}

type RemoveUserFromProjectReq struct {
	Reason    string `json:"reason" binding:"required"`
	TenantID  uint64 `json:"tenant_id,string" binding:"required"`
	ProjectID uint64 `json:"project_id,string" binding:"required"`
	UserID    uint64 `json:"user_id,string" binding:"required"`
	UserEmail string `json:"user_email"`
}

type AddImpersonateReq struct {
	TenantID uint64 `json:"tenant_id,string" binding:"required"`
	Reason   string `json:"reason" binding:"required"`
	ExpireAt int64  `json:"expire_at" binding:"required"`
}

type AddImpersonateResp struct {
	api.BaseResp

	InstanceID *uint64 `json:"instance_id"` // the operation instance id
}

type GetImpersonateResp struct {
	api.BaseResp

	User       string  `json:"user"`
	InstanceID *uint64 `json:"instance_id"`      // the operation instance id
	TenantID   *uint64 `json:"tenant_id,string"` // impersonating tenant id for the user
}

type AddCreditsReq struct {
	Department      string           `json:"department"`
	ExpiredDate     string           `json:"expired_date"`
	TotalCredits    string           `json:"total_credits"`
	UnitFilters     []UnitFilterSlim `json:"unit_filters"`
	Usage           string           `json:"usage"`
	ApplyForTest    bool             `json:"apply_for_test"`
	Reason          string           `json:"reason"`
	DiscountEnabled bool             `json:"discount_enabled"`
	UseCase         string           `json:"use_case"`
}

type AddCreditsInternalReq struct {
	StartDate       string           `json:"start_date" binding:"required"`
	ExpiredDate     string           `json:"expired_date" binding:"required"`
	TotalCredits    string           `json:"total_credits" binding:"required"`
	Usage           string           `json:"usage" binding:"required"`
	Note            string           `json:"note" binding:"required"`
	UnitFilters     []UnitFilterSlim `json:"unit_filters" binding:"required"`
	DiscountEnabled bool             `json:"discount_enabled"`
	ForeignID       string           `json:"foreign_id"`
}

type RecalculateBillsReq struct {
	SourceRunID          string `json:"source_run_id"`
	OverwriteCurrentCost bool   `json:"overwrite_current_cost"`
	Reason               string `json:"reason" binding:"required"`
	RecalculateCreditsReq
}

type RecalculateInvoiceReq struct {
	Reason string `json:"reason" binding:"required"`
	RecalculateCreditsReq
}

type RecalculateCreditsReq struct {
	CreditsTotalCredits    string           `json:"credits_total_credits"`
	CreditsDiscountEnabled bool             `json:"credits_discount_enabled"`
	CreditsExpiredDate     string           `json:"credits_expired_date"`
	CreditsProvider        string           `json:"credits_provider"`
	CreditsStartDate       string           `json:"credits_start_date"`
	CreditsUnitFilters     []UnitFilterSlim `json:"credits_unit_filters"`
}

type ListTenantQuotaResp struct {
	TenantQuotas []*TenantQuota `json:"tenant_quotas"`
}

type TenantQuota struct {
	Component ComponentType `json:"component"`
	Quota     uint32        `json:"quota"`
}

type UnitFilterSlim struct {
	FilterID uint   `json:"filter_id"`
	Name     string `json:"name"`
	TypeID   uint64 `json:"type_id,omitempty"`
}

type TenantResponse struct {
	Id              uint64                    `json:"id,string"`
	CreatedAt       *time.Time                `json:"created_at"`
	Name            string                    `json:"name"`
	Alias           string                    `json:"alias"`
	Email           string                    `json:"email"`
	SalesOwnerEmail string                    `json:"sales_owner_email"`
	Country         string                    `json:"country"`
	TimeZone        string                    `json:"time_zone"`
	Status          tntent.TenantStatus       `json:"status"`
	Kind            tntent.TenantKind         `json:"kind"`
	Plan            tntent.TenantPlan         `json:"plan"`
	Region          tntent.TenantRegion       `json:"region"`
	CustomerID      string                    `json:"customer_id"`
	Segmentation    tntent.TenantSegmentation `json:"segmentation"`
	TAMName         string                    `json:"tam_name"`
}

type MetricsQueryResponse struct {
	Status string                   `json:"status"`
	Data   *tntent.MetricsQueryData `json:"data"`
}

type MetricsQueryRequest struct {
	Query string `form:"query"`
	Start int64  `form:"start"`
	End   int64  `form:"end"`
	Step  string `form:"step"`
}

type ListByocRegionsResp struct {
	Regions []*Region `json:"regions"`
}

type Region struct {
	ID                 string                    `json:"id"`
	CloudProvider      string                    `json:"cloud_provider"`
	DisplayName        string                    `json:"display_name"`
	State              string                    `json:"state"`
	ServiceAddress     string                    `json:"service_address"`
	LogicalName        string                    `json:"logical_name"`
	ServicePlan        string                    `json:"service_plan"`
	SharedBucketUrl    string                    `json:"shared_bucket_url"`
	OpsAuditLogSetting *RegionOpsAuditLogSetting `json:"ops_audit_log_setting"`
}

type RegionOpsAuditLogSetting struct {
	Enabled     bool         `json:"enabled"`
	LogLocation *LogLocation `json:"log_location"`
	LogRotation *LogRotation `json:"log_rotation"`
}

type UpdateRegionOpsAuditLogSettingReq struct {
	RegionLogicalName string       `json:"region_logical_name"  binding:"required"`
	Enabled           *bool        `json:"enabled" binding:"required"`
	LogLocation       *LogLocation `json:"log_location"`
	LogRotation       *LogRotation `json:"log_rotation"`
	Reason            string       `json:"reason" binding:"required"`
}

type LogLocation struct {
	Type       string `json:"type"`
	BucketUri  string `json:"bucket_uri"`
	RoleArn    string `json:"role_arn"`
	AccountId  string `json:"account_id"`
	ExternalId string `json:"external_id"`
}

type LogRotation struct {
	FileSizeLimitInMb     uint64 `json:"file_size_limit_in_mb"`
	RotationIntervalInMin uint64 `json:"rotation_interval_in_min"`
}

func BuildTenantQuotas(tenantQuotas []*core.TenantQuota) ListTenantQuotaResp {
	quotas := make([]*TenantQuota, 0)
	for _, q := range tenantQuotas {
		quotas = append(quotas, &TenantQuota{
			Component: BuildComponentType(q.Component),
			Quota:     q.Quota,
		})
	}
	return ListTenantQuotaResp{
		TenantQuotas: quotas,
	}
}

type ComponentType string

const (
	ComponentTypeTikv      ComponentType = "tikv"
	ComponentTypeTidb      ComponentType = "tidb"
	ComponentTypeTiflash   ComponentType = "tiflash"
	ComponentTypePd        ComponentType = "pd"
	ComponentTypeTiproxy   ComponentType = "tiproxy"
	ComponentTypeUndefined ComponentType = "undefined"
)

func BuildComponentType(componentType core.ComponentType) ComponentType {
	switch componentType {
	case core.ComponentType_COMPONENT_TYPE_PD:
		return ComponentTypePd
	case core.ComponentType_COMPONENT_TYPE_TIDB:
		return ComponentTypeTidb
	case core.ComponentType_COMPONENT_TYPE_TIKV:
		return ComponentTypeTikv
	case core.ComponentType_COMPONENT_TYPE_TIFLASH:
		return ComponentTypeTiflash
	case core.ComponentType_COMPONENT_TYPE_TIPROXY:
		return ComponentTypeTiproxy
	default:
		return ComponentTypeUndefined
	}
}

func BuildRegions(resp *mgmtv1.ListRegionsResponse) *ListByocRegionsResp {
	if resp == nil {
		return &ListByocRegionsResp{}
	}

	apiResp := &ListByocRegionsResp{
		Regions: make([]*Region, 0, len(resp.Regions)),
	}

	for _, region := range resp.Regions {
		if region == nil {
			continue
		}
		if region.Region == nil {
			continue
		}

		apiRegion := &Region{
			ID:              region.Region.Id.String(),
			CloudProvider:   region.Region.CloudProviderId.String(),
			DisplayName:     region.Region.DisplayName,
			State:           region.Region.State.String(),
			ServiceAddress:  region.Region.ServiceAddress,
			LogicalName:     region.Region.LogicalName,
			ServicePlan:     region.Region.GetServicePlan().String(),
			SharedBucketUrl: getSharedBucketUrl(region.Region.SharedBucketUrl),
		}
		if region.Region.OpsAuditLogSetting != nil {
			apiRegion.OpsAuditLogSetting = &RegionOpsAuditLogSetting{
				Enabled: region.Region.OpsAuditLogSetting.Enabled,
			}
			if region.Region.OpsAuditLogSetting.LogLocation != nil {
				location := region.Region.OpsAuditLogSetting.LogLocation
				apiRegion.OpsAuditLogSetting.LogLocation = &LogLocation{
					Type:       location.Type.String(),
					BucketUri:  location.BucketUri,
					RoleArn:    location.RoleArn,
					AccountId:  location.AccountId,
					ExternalId: location.ExternalId,
				}
			}
			if region.Region.OpsAuditLogSetting.LogRotation != nil {
				rotation := region.Region.OpsAuditLogSetting.LogRotation
				apiRegion.OpsAuditLogSetting.LogRotation = &LogRotation{
					FileSizeLimitInMb:     rotation.FileSizeLimitInMb,
					RotationIntervalInMin: rotation.RotationIntervalInMin,
				}
			}
		}

		apiResp.Regions = append(apiResp.Regions, apiRegion)
	}

	return apiResp
}

func getSharedBucketUrl(sharedBucketUrl *string) string {
	if sharedBucketUrl == nil {
		return ""
	} else {
		return *sharedBucketUrl
	}
}
