package entity

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"time"

	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
)

//go:generate ../../tools/bin/goqueryset -in entity.go

// for compatible to mysql, utf8mb4 index should not greater than 191
// gen:qs
type Unit struct {
	UnitKey         string `gorm:"type:varchar(128);primarykey"`
	Description     string `gorm:"type:varchar(255)"`
	Category        string `gorm:"type:varchar(255)"`
	Provider        string `gorm:"type:varchar(255)"`
	Region          string `gorm:"type:varchar(255)"`
	CreatedAt       time.Time
	UpdatedAt       time.Time
	Price           decimal.Decimal `gorm:"type:decimal(30, 15)"`
	EditionCode     string          `gorm:"type:varchar(256)"`
	CommodityCode   string          `gorm:"type:varchar(64)"`
	PriceType       string          `gorm:"type:varchar(64)"`
	PriceExpression Labels          `gorm:"type:json"`
	MeasureUnit     string          `gorm:"type:varchar(255)"`
	SkuType         string          `gorm:"type:varchar(64)"`
	SkuTemplateID   uint
	ProviderPrice   decimal.Decimal `gorm:"type:decimal(30, 15)"`
	ProviderVersion string          `gorm:"type:varchar(255)"`
	GroupId         uint
	Version         uint
	BetaEndTime     *time.Time
	BetaInternal    bool `gorm:"not null;default:false"`
	UnitEndTime     *time.Time
	ModifierAndCreator
}

const (
	PriceType_Normal    = "normal"
	PriceType_Threshold = "threshold"
	PriceType_Interval  = "interval"
)
const (
	PriceType_Threshold_label = "multiple_factor_label"
	PriceType_Threshold_value = "multiple_factor_threshold"
	PriceType_IntervalMin     = "intervalMin"
	PriceType_IntervalMax     = "intervalMax"
	PriceType_IntervalValue   = "intervalValue"
)
const (
	SkuType_Normal = "normal"
	SkuType_Test   = "testing"
)
const (
	SKUGroupType_FormulaPrice = "formula_price"
	SKUGroupType_ManualPrice  = "manual_price"
)

// gen:qs
type SKUGroups struct {
	gorm.Model
	UnitKeyPattern  string `gorm:"type:varchar(512);"`
	Name            string `gorm:"type:varchar(255)"`
	Category        string `gorm:"type:varchar(255)"`
	SKUGroupType    string `gorm:"type:varchar(64);not null;default:'formula_price'"`
	GroupLabels     Labels `gorm:"type:json"`
	PriceFormula    string `gorm:"type:varchar(1024)"`
	PriceType       string `gorm:"type:varchar(64)"`
	PriceExpression Labels `gorm:"type:json"`
	MeasureUnit     string `gorm:"type:varchar(255)"`
}
type PackageUnits forms.UnitPrices

type Labels map[string]string
type OrderMap map[string]int32
type AdjustmentMeta map[string]string
type PaymentMeta forms.PaymentMeta
type InvoiceMeta forms.InvoiceMeta

// MergedLabelEntry captures a single raw cost contribution to a merged UserUnitCost row.
// It is appended every time a merge path (txCost / mergeRawUnitCost) folds a raw cost
// into a merged row of a label-skipping unit so that downstream code (notably MSP/CPPO
// SecondaryMeasure) can replay each contribution with its original labels (e.g.
// PriceType_IntervalValue) preserved. CostID keeps the source raw costID so a replayed
// transfer can be traced back to the customer-side raw row.
type MergedLabelEntry struct {
	CostID         string          `json:"cost_id"`
	Cost           int             `json:"cost"`
	CostPrice      decimal.Decimal `json:"cost_price"`
	RawPrice       decimal.Decimal `json:"raw_price"`
	Adjustment     decimal.Decimal `json:"adjustment"`
	AdjustmentMeta AdjustmentMeta  `json:"adjustment_meta"`
	Labels         Labels          `json:"labels"`
	CostTimeStamp  int64           `json:"cost_time_stamp"`
}

type MergedLabelsHistory []MergedLabelEntry

// Value implements driver.Valuer: GORM's Updates(struct) path does not apply
// the JSON serializer to named slice types, so without this the merged-row
// UPDATE fails with "unsupported type entity.MergedLabelEntry, a struct".
func (h MergedLabelsHistory) Value() (driver.Value, error) {
	if h == nil {
		return nil, nil
	}
	return json.Marshal(h)
}

// Scan implements sql.Scanner for GORM raw queries (e.g. the transfer query
// in model/costexplorer) scanning the merged_labels_history JSON column.
func (h *MergedLabelsHistory) Scan(value interface{}) error {
	if value == nil {
		*h = nil
		return nil
	}
	var data []byte
	switch v := value.(type) {
	case []byte:
		data = v
	case string:
		data = []byte(v)
	default:
		return fmt.Errorf("unsupported type %T for MergedLabelsHistory", value)
	}
	return json.Unmarshal(data, h)
}

type ExpressionLabels []forms.ExpressionLabel
type PricingInfos []forms.PricingInfo

type ClosePolicy forms.ClosePolicy
type CloseActionsStatus forms.CloseActionsStatus

type UnitFilters []*forms.UnitFilter

type RechargeCreditsPlanRule forms.RechargeCreditsPlanRule

type BillingAddress forms.BillingAddress

type CopyUnitFilterArgs forms.CopyUnitFilterArgs

type TieredPriceRules []forms.TieredPriceRule

const (
	PackageMaxBindedTimes_INF = 0
)

type PackageContent struct {
	Description          string           `gorm:"type:varchar(255)"`
	PackageUnits         PackageUnits     `gorm:"type:json"`
	Credits              decimal.Decimal  `gorm:"type:decimal(30, 15)"`
	CreditsEnable        bool             `gorm:"not null"`
	CreditsOnly          bool             `gorm:"not null"`
	Discount             decimal.Decimal  `gorm:"type:decimal(30, 15)"`
	Labels               Labels           `gorm:"type:json"`
	ExpressionLabels     ExpressionLabels `gorm:"type:json"`
	ExtraPricingInfos    PricingInfos     `gorm:"type:json"`
	TenantMaxBindedTimes uint
}

// gen:qs
type Package struct {
	gorm.Model
	LatestVersion uint `gorm:"not null"`
	PackageContent
}

// Create and read only entity, do not update this
// gen:qs
type PackageVersion struct {
	PackageID uint `gorm:"primarykey,priority:1;autoIncrement:false"`
	Version   uint `gorm:"primarykey,priority:2;autoIncrement:false"`
	CreatedAt time.Time
	PackageContent
}

const (
	BindedPackageStatus_Active   = "normal"
	BindedPackageStatus_InActive = "inactive"
)

//gen:qs
type UnitFilter struct {
	gorm.Model
	Name           string `gorm:"type:varchar(128);"`
	Description    string `gorm:"type:varchar(255)"`
	General        bool
	Provider       string `gorm:"type:varchar(255);not null;default:'all'"`
	Region         string `gorm:"type:varchar(255);not null;default:'all'"`
	Category       string `gorm:"type:varchar(255);not null;default:'all'"`
	UnitKeyPattern string `gorm:"type:varchar(255);not null;default:''"`
	Labels         Labels `gorm:"type:json"`
	ParentID       uint
	Internal       bool    `gorm:"not null;default:0;index"`
	UniqueKey      *string `gorm:"type:varchar(128);uniqueIndex"`
	Priority       int
	// STORED GENERATED column. See AGENTS.md "FORCE INDEX on
	// STORED GENERATED column indexes" for the rule that any
	// query filtering on this column must carry
	// `FORCE INDEX (idx_ct_labels_cluster_id)`. The index is
	// not implied by the gorm tag (the `->` prefix and the
	// gormigrate v2 fresh-DB footgun mean AutoMigrate will
	// not create it); if a future query needs it, the column
	// and the index must both be added by a follow-up
	// migration.
	ClusterID string `gorm:"->;type:VARCHAR(32) GENERATED ALWAYS AS (JSON_UNQUOTE(labels->'$.clusterID')) STORED"`
}

func (*UnitFilter) TableName() string {
	return "credit_types"
}

// gen:qs
type UnitFilterSelection struct {
	gorm.Model
	UnitFilterID uint   `gorm:"uniqueIndex:udx_target_filter_id,priority:2"`
	Target       string `gorm:"type:varchar(64);uniqueIndex:udx_target_filter_id,priority:1"`
}

// gen:qs
type BindedPackage struct {
	gorm.Model
	TenantID               uint             `gorm:"index:user_status,priority:1;index:user_package,priority:1;index:tenant_status_internal,priority:1;index:tenant_status_billing_config,priority:1"`
	PackageID              uint             `gorm:"index:package_id;index:user_package,priority:2"`
	BindedDiscount         *decimal.Decimal `gorm:"type:decimal(30, 15)"`
	BindedDiscountRecordID *uint
	DiscountEnabled        bool
	StartedDate            *time.Time      `gorm:"index:validity_period,priority:3"`
	ExpiredDate            *time.Time      `gorm:"index:validity_period,priority:1"`
	ClosedDate             *time.Time      `gorm:"index:validity_period,priority:2"`
	RemainingCredits       decimal.Decimal `gorm:"type:decimal(30, 15)"`
	OldUnitFilters         UnitFilters     `gorm:"column:unit_filters;type:json"`
	UnitFilters            []*UnitFilter   `gorm:"many2many:unit_filter_binded_packages;"`
	// Internal participates in the (tenant_id, status, internal)
	// composite index added by migration
	// 2026-06-22-binded-packages-measure-indexes. The DB-side
	// measure filter relies on it for the OR-rewrite (UNION ALL
	// branch for "internal = 1").
	Internal       bool       `gorm:"default:0;index:tenant_status_internal,priority:3"`
	Active         string     `gorm:"column:status;not null;index:user_status,priority:2;index:tenant_status_internal,priority:2;index:tenant_status_billing_config,priority:2"`
	InactiveTime   *time.Time `gorm:"index"`
	InvoicePayable bool       `gorm:"default:0"`
	// BindedBillingConfigID participates in the
	// (tenant_id, status, binded_billing_config_id) composite index
	// added by the same migration. Used by the OR-rewrite
	// UNION ALL branch for "internal <> 1 AND
	// binded_billing_config_id = ?".
	BindedBillingConfigID *uint `gorm:"index:tenant_status_billing_config,priority:3"`
}

const (
	CreditsChargeType_MANUAL = "manual"
	CreditsChargeType_SYSTEM = "system"
)

// gen:qs
type CreditsChargeRecord struct {
	ID              uint `gorm:"primarykey"`
	CreatedAt       time.Time
	ChargeType      string
	ChargeReason    string
	ChargeCount     decimal.Decimal `gorm:"type:decimal(30, 15)"`
	BindedPackageID uint
	ChargerTenantID uint
	ChargerUserID   uint
}

// gen:qs
type BindedDiscountChangeRecord struct {
	ID              uint `gorm:"primarykey"`
	CreatedAt       time.Time
	ChangeReason    string
	BindedDiscount  *decimal.Decimal `gorm:"type:decimal(30, 15)"`
	BindedPackageID uint
	ChangerTenantID uint
	ChangerUserID   uint
}

const (
	UserUnitCostStatus_INIT       = "init"
	UserUnitCostStatus_DONE       = "done"
	UserUnitCostStatus_Failed     = "failed"
	UserUnitCostStatus_Merged     = "merged"
	UserUnitCostStatus_Recal      = "recal"
	UserUnitCostStatus_Superseded = "superseded"
	UserUnitCostType_Raw          = "raw"
	UserUnitCostType_Merged       = "merged"
)

// gen:qs
type UserUnitCost struct {
	CostID        string `gorm:"primarykey"`
	Unit          string `gorm:"not null"`
	Cost          int    `gorm:"not null"`
	CostTimeStamp int64  `gorm:"not null;index:timestamp;index:tenant_timestamp_status,priority:2;index:tenant_timestamp_status_cluster,priority:2"`
	TenantID      uint   `gorm:"index:user_invoice,priority:1;index:tenant_timestamp_status,priority:1;index:tenant_timestamp_status_cluster,priority:1"`
	Labels        Labels `gorm:"type:json"`

	CostPrice                    decimal.Decimal `gorm:"type:decimal(30, 15)"`
	RawPrice                     decimal.Decimal `gorm:"type:decimal(30, 15)"`
	BindedPackageID              uint            `gorm:"not null;index:binded_package_id"`
	PackageID                    uint
	PackageVersion               uint
	BindedDiscountChangeRecordID *uint
	Status                       string `gorm:"type:varchar(64);index:tenant_timestamp_status,priority:3;index:tenant_timestamp_status_cluster,priority:3"`
	ConsumedSource               string `gorm:"not null"`
	InvoiceID                    int    `gorm:"not null;index:user_invoice,priority:2"`
	Type                         string `gorm:"not null;default:'raw'"`
	EditionCode                  string `gorm:"type:varchar(256)"`
	CommodityCode                string `gorm:"type:varchar(64)"`
	CreatedAt                    time.Time
	UpdatedAt                    time.Time
	Discount                     decimal.Decimal `gorm:"default:'1'"` // do not change db type here, it may cause measure failed in ddl running
	DiscountID                   uint
	DiscountVersionID            uint
	SkuPrice                     decimal.Decimal `gorm:"type:decimal(30, 15)"`
	SkuVersionID                 uint
	Adjustment                   decimal.Decimal `gorm:"type:decimal(30, 15)"`
	AdjustmentMeta               AdjustmentMeta
	Internal                     bool   `gorm:"default:0"`
	ClusterID                    string `gorm:"->;type:varchar(32) GENERATED ALWAYS AS (JSON_UNQUOTE(labels->'$.clusterID')) STORED;index:tenant_timestamp_status_cluster,priority:4"`
	BindedBillingConfigID        uint
	// MergedLabelsHistory records the per-raw-cost breakdown that was folded into this
	// merged row. Only populated for units that skip labels on merge
	// (UnitSkipLabelsOnMerge) when config.EnableMergedLabelsHistory is on. Used by
	// MSP/CPPO SecondaryMeasure to replay each contribution with its original labels so
	// pricing (e.g. PriceType_IntervalValue) is computed per-contribution, not on the
	// stale merged row's labels.
	MergedLabelsHistory MergedLabelsHistory `gorm:"type:json"`
}

// gen:qs
type RawUserUnitCost struct {
	UserUnitCost
	MergedCostID *string `gorm:"type:varchar(191)"`
}

const (
	ConsumedSource_Credits = "credits"
	ConsumedSource_Invoice = "invoice"
	ConsumedSource_None    = "none"
)

const (
	PaymentType_AWS             = "aws"
	PaymentType_GCP             = "gcp"
	PaymentType_AZURE           = "azure"
	PaymentType_STRIPE          = "stripe"
	PaymentType_WireTransfer    = "wire_transfer"
	PaymentType_MSP             = "msp"
	PaymentType_AWSCPPOTransfer = "aws_cppo_transfer"
	PaymentType_AWSCPPOReseller = "aws_cppo_reseller"
	PaymentType_GCPMCPOTransfer = "gcp_mcpo_transfer"
	PaymentType_GCPMCPOReseller = "gcp_mcpo_reseller"
	PaymentType_AliCloud        = "alicloud"
	PaymentType_Resale          = "resale"
)

const (
	PaymentMethodStatus_Active   = "active"
	PaymentMethodStatus_InActive = "inactive"
)

// gen:qs
type PaymentMethod struct {
	gorm.Model
	TenantID              uint         `gorm:"index:tenant_status,priority:1;uniqueIndex:uidx_tenant_id_payment_type,priority:3"`
	PaymentType           string       `gorm:"type:varchar(32);not null;uniqueIndex:uidx_tenant_id_payment_type,priority:1"`
	PaymentCode           string       `gorm:"type:varchar(64);not null"`
	PaymentSubCode        *string      `gorm:"type:varchar(64)"`
	Payable               bool         `gorm:"not null;default:false"`
	ManagedBySales        bool         `gorm:"not null;default:false"`
	Status                string       `gorm:"type:varchar(128);not null;default:'active';index:tenant_status,priority:2"`
	UnpayableAt           *time.Time   `gorm:"index"`
	BindedBillingConfigID uint         `gorm:"uniqueIndex:uidx_tenant_id_payment_type,priority:2"`
	Meta                  *PaymentMeta `gorm:"type:json;"`

	Invoiceable   *bool `gorm:"default:true"`
	InvoiceableAt *time.Time
}

// gen:qs
type PaymentMethodActiveRecord struct {
	gorm.Model
	TenantID        uint `gorm:"index"`
	PaymentMethodID uint
}

const (
	InvoiceStatus_INITED                = "inited"
	InvoiceStatus_GENERATED             = "generated"
	InvoiceStatus_CONFIRMED             = "confirmed"
	InvoiceStatus_SENT                  = "sent"
	InvoiceStatus_PAID                  = "paid" // for now, we do not use this status
	InvoiceStatus_SUSPENDED             = "suspended"
	InvoiceStatus_SPLIT                 = "split"
	InvoiceStatus_BELOW_THRESHOLD_CARRY = "below_threshold_carry"
	InvoiceStatus_EXPIRED_CARRYOVER     = "expired_carryover"
)

const (
	InvoiceType_MONTHLY      = "monthly"
	InvoiceType_INMONTH      = "inmonth"
	InvoiceType_COSTQUOTA    = "cost_quota"
	InvoiceType_Recal        = "recal"
	InvoiceType_DAILY        = "daily"
	InvoiceType_HOURLY       = "hourly"
	InvoiceType_COMMITMENT   = "commitment"
	InvoiceType_SUBORDINATED = "subordinated" // sub-invoice
)

const (
	InvoiceUnbindID int = -1
	InvoiceNoUseID  int = -2
)

// gen:qs
type MultiPaymentCode struct {
	PaymentCode string          `gorm:"type:varchar(128)"`
	Discount    decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

// gen:qs
type Invoice struct {
	gorm.Model
	TenantID              uint            `gorm:"index:user_status,priority:1;index:user_date,priority:1"`
	Status                string          `gorm:"index:user_status,priority:2"`
	Type                  string          `gorm:"not null"`
	InvoiceDate           time.Time       `gorm:"not null;index;index:user_date,priority:2"`
	SentDate              *time.Time      `gorm:"index:idx_payment_id_sent_date,priority:1"`
	CostPrice             decimal.Decimal `gorm:"type:decimal(30, 15)"`
	RawPrice              decimal.Decimal `gorm:"type:decimal(30, 15)"`
	PaymentMethodID       *uint           `gorm:"index;index:idx_payment_id_sent_date,priority:2"`
	TaxRateID             *string         `gorm:"type:varchar(36)"`
	BindedBillingConfigID uint
	Meta                  *InvoiceMeta    `gorm:"column:payment_meta;type:json"`
	ChannelLoss           decimal.Decimal `gorm:"type:decimal(30, 15);default:0"`
	PaymentType           *string         `gorm:"type:varchar(64)"`
	PaymentCode           *string         `gorm:"type:varchar(64)"`
	ParentInvoiceID       uint            `gorm:"index"`
	StripeInvoiceID       *string         `gorm:"type:varchar(128);index"`
	ChargeMode            *string         `gorm:"type:varchar(32);index"` // "auto" or "manual"
}

func (Invoice) TableName() string {
	return "billing2_invoices"
}

const (
	BillingSnapshotRunStatusInit    = "init"
	BillingSnapshotRunStatusRunning = "running"
	BillingSnapshotRunStatusSuccess = "success"
	BillingSnapshotRunStatusFailed  = "failed"
	BillingSnapshotRunStatusFrozen  = "frozen"

	BillingSnapshotInputBundleTypeBaseRun        = "base_run"
	BillingSnapshotInputBundleTypeRerunCandidate = "rerun_candidate"
	BillingSnapshotInputBundleTypeRerunPublished = "rerun_published"

	BillingSnapshotRunSourceModeSnapshotPrimary = "snapshot_primary"

	BillingSnapshotActiveRunDomainInvoice    = "invoice"
	BillingSnapshotSelectionModeAuto         = "auto"
	BillingSnapshotSelectionModeManual       = "manual"
	BillingSnapshotSelectionModeRerunPublish = "rerun_publish"

	BillingSnapshotInvoiceDispositionNormal     = "normal"
	BillingSnapshotInvoiceDispositionSuperseded = "superseded"
	BillingSnapshotInvoiceDispositionAdjustment = "adjustment"

	BillingSnapshotApplyModeReplaceUnissuedInvoice  = "replace_unissued_invoice"
	BillingSnapshotApplyModeCreateAdjustmentInvoice = "create_adjustment_invoice"

	BillingSnapshotApplyStatusPreviewed      = "previewed"
	BillingSnapshotApplyStatusApplying       = "applying"
	BillingSnapshotApplyStatusCreditsApplied = "credits_applied"
	BillingSnapshotApplyStatusProjected      = "projected"
	BillingSnapshotApplyStatusPublished      = "published"
	BillingSnapshotApplyStatusFailed         = "failed"

	MeteringStatusToPrice          = "to_price"
	MeteringStatusPricing          = "pricing"
	MeteringStatusPriced           = "priced"
	MeteringStatusPriceFailed      = "price_failed"
	MeteringStatusPriceRetryFailed = "price_retry_failed"
	MeteringStatusPriceIgnore      = "price_ignore"
)

type BillingSnapshotInputBundle struct {
	BundleID        uint      `gorm:"primarykey;column:bundle_id"`
	TenantID        uint      `gorm:"not null;index:idx_billing_snapshot_input_bundle_scope,priority:1"`
	BillingConfigID uint      `gorm:"not null;index:idx_billing_snapshot_input_bundle_scope,priority:2"`
	PeriodMonth     string    `gorm:"type:char(7);not null;index:idx_billing_snapshot_input_bundle_scope,priority:3"`
	BundleType      string    `gorm:"type:varchar(32);not null;index:idx_billing_snapshot_input_bundle_scope,priority:4"`
	InputRefs       Labels    `gorm:"type:json;not null"`
	OpeningStateRef *string   `gorm:"type:varchar(128)"`
	PatchRef        *string   `gorm:"type:varchar(128)"`
	ContentHash     *string   `gorm:"type:varchar(128);index"`
	CreatedAt       time.Time `gorm:"not null"`
}

func (BillingSnapshotInputBundle) TableName() string {
	return "billing_snapshot_input_bundle"
}

type BillingSnapshotRun struct {
	RunID              uint      `gorm:"primarykey;column:run_id"`
	TenantID           uint      `gorm:"not null;index:idx_billing_snapshot_run_scope,priority:1"`
	BillingConfigID    uint      `gorm:"not null;index:idx_billing_snapshot_run_scope,priority:2"`
	PeriodMonth        string    `gorm:"type:char(7);not null;index:idx_billing_snapshot_run_scope,priority:3"`
	RunType            string    `gorm:"type:varchar(16);not null;index:idx_billing_snapshot_run_scope,priority:4"`
	InvoiceDate        time.Time `gorm:"not null;index:idx_billing_snapshot_run_scope,priority:5"`
	WindowStart        time.Time `gorm:"not null"`
	WindowEnd          time.Time `gorm:"not null"`
	Status             string    `gorm:"type:varchar(16);not null;index:idx_billing_snapshot_run_status_created,priority:1"`
	MeteringSnapshotID uint      `gorm:"not null;index:idx_billing_snapshot_run_metering_snapshot"`
	InputBundleID      uint      `gorm:"not null;default:0;index:idx_billing_snapshot_run_input_bundle"`
	VersionSet         Labels    `gorm:"type:json;not null"`
	RerunRequestID     *uint     `gorm:"index"`
	RerunOfRunID       *uint     `gorm:"index"`
	RerunWindowStart   *time.Time
	RerunWindowEnd     *time.Time
	SourceMode         string    `gorm:"type:varchar(32);not null"`
	ErrorMessage       *string   `gorm:"type:varchar(512)"`
	CreatedAt          time.Time `gorm:"not null;index:idx_billing_snapshot_run_status_created,priority:2"`
	FinishedAt         *time.Time
}

func (BillingSnapshotRun) TableName() string {
	return "billing_snapshot_run"
}

type BillingSnapshotSettlementFact struct {
	ID              uint            `gorm:"primarykey"`
	RunID           uint            `gorm:"not null;index:idx_billing_snapshot_settlement_fact_run;index:idx_billing_snapshot_settlement_fact_run_invoice,priority:1"`
	TenantID        uint            `gorm:"not null;index:idx_billing_snapshot_settlement_fact_run_invoice,priority:2;index:idx_billing_snapshot_settlement_fact_tenant_period,priority:1"`
	BillingConfigID uint            `gorm:"not null;index:idx_billing_snapshot_settlement_fact_run_invoice,priority:3;index:idx_billing_snapshot_settlement_fact_tenant_period,priority:2"`
	BilledDate      time.Time       `gorm:"type:date;not null;index:idx_billing_snapshot_settlement_fact_run_invoice,priority:4;index:idx_billing_snapshot_settlement_fact_tenant_period,priority:3"`
	Unit            string          `gorm:"type:varchar(128);not null"`
	Labels          Labels          `gorm:"type:json"`
	ConsumedSource  string          `gorm:"type:varchar(32);not null"`
	UsageAmount     int             `gorm:"not null;default:0"`
	RawPrice        decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	DiscountPrice   decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	CreditPrice     decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	CostPrice       decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	SourceItemRef   *string         `gorm:"type:varchar(128)"`
	IsInternal      bool            `gorm:"not null;default:0"`
	CreatedAt       time.Time       `gorm:"not null"`
}

func (BillingSnapshotSettlementFact) TableName() string {
	return "billing_snapshot_settlement_fact"
}

type BillingSnapshotActiveRun struct {
	ID              uint      `gorm:"primarykey"`
	TenantID        uint      `gorm:"not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:1"`
	BillingConfigID uint      `gorm:"not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:2"`
	Domain          string    `gorm:"type:varchar(32);not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:3"`
	PeriodMonth     string    `gorm:"type:char(7);not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:4"`
	RunType         string    `gorm:"type:varchar(16);not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:5"`
	InvoiceDate     time.Time `gorm:"not null;uniqueIndex:uk_billing_snapshot_active_invoice,priority:6"`
	SelectedRunID   uint      `gorm:"not null;index:idx_billing_snapshot_active_run_selected"`
	SelectionMode   string    `gorm:"type:varchar(16);not null"`
	SelectedReason  *string   `gorm:"type:varchar(255)"`
	CreatedAt       time.Time `gorm:"not null"`
	UpdatedAt       time.Time `gorm:"not null"`
}

func (BillingSnapshotActiveRun) TableName() string {
	return "billing_snapshot_active_run"
}

// BillingSnapshotInvoiceLink binds one rerun run to one invoice. An invoice
// can carry many links (one per applied rerun layer); the overlay read path
// resolves overlapping windows per sub-segment in favour of the most recently
// applied layer (highest link ID). The (invoice_id, run_id) unique index is
// the idempotency guard: the same run can only ever be one layer on a given
// invoice.
type BillingSnapshotInvoiceLink struct {
	ID                  uint            `gorm:"primarykey"`
	InvoiceID           uint            `gorm:"not null;uniqueIndex:uk_billing_snapshot_invoice_link_invoice_run,priority:1;index:idx_billing_snapshot_invoice_link_invoice"`
	RunID               uint            `gorm:"not null;uniqueIndex:uk_billing_snapshot_invoice_link_invoice_run,priority:2;index:idx_billing_snapshot_invoice_link_run"`
	TenantID            uint            `gorm:"not null;index:idx_billing_snapshot_invoice_link_tenant_month,priority:1"`
	BillingConfigID     uint            `gorm:"not null;index:idx_billing_snapshot_invoice_link_tenant_month,priority:2"`
	InvoiceType         string          `gorm:"type:varchar(16);not null;index:idx_billing_snapshot_invoice_link_tenant_month,priority:4"`
	InvoiceDate         time.Time       `gorm:"not null;index:idx_billing_snapshot_invoice_link_tenant_month,priority:5"`
	InvoiceMonth        string          `gorm:"type:char(7);not null;index:idx_billing_snapshot_invoice_link_tenant_month,priority:3"`
	Amount              decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	Disposition         string          `gorm:"type:varchar(16);not null;default:normal"`
	SupersedesInvoiceID *uint           `gorm:"index"`
	SentDate            *time.Time      `gorm:"index"`
	CreatedAt           time.Time       `gorm:"not null"`
}

func (BillingSnapshotInvoiceLink) TableName() string {
	return "billing_snapshot_invoice_link"
}

type BillingSnapshotApplyRecord struct {
	ApplyID                  uint      `gorm:"primarykey;column:apply_id"`
	TenantID                 uint      `gorm:"not null;index:idx_billing_snapshot_apply_scope,priority:1"`
	BillingConfigID          uint      `gorm:"not null;index:idx_billing_snapshot_apply_scope,priority:2"`
	InvoiceID                *uint     `gorm:"index"`
	AdjustmentInvoiceID      *uint     `gorm:"index"`
	SourceRunID              uint      `gorm:"not null;index"`
	BaseRunID                *uint     `gorm:"index"`
	BaseInvoiceID            *uint     `gorm:"index"`
	ApplyMode                string    `gorm:"type:varchar(32);not null;index:idx_billing_snapshot_apply_scope,priority:3"`
	PatchRef                 string    `gorm:"type:varchar(128);not null;uniqueIndex"`
	Status                   string    `gorm:"type:varchar(32);not null;index:idx_billing_snapshot_apply_scope,priority:4"`
	CreditsOpeningStateRef   *string   `gorm:"type:varchar(128)"`
	CreditsOpeningState      Labels    `gorm:"type:json"`
	CreditsAllocationSummary Labels    `gorm:"type:json"`
	LegacyProjectionRef      *string   `gorm:"type:varchar(128);index"`
	DiffSummary              Labels    `gorm:"type:json"`
	ErrorMessage             *string   `gorm:"type:varchar(512)"`
	CreatedAt                time.Time `gorm:"not null"`
	UpdatedAt                time.Time `gorm:"not null"`
	FinishedAt               *time.Time
}

func (BillingSnapshotApplyRecord) TableName() string {
	return "billing_snapshot_apply_record"
}

type MeteringEvent struct {
	ID            int64           `gorm:"primaryKey;autoIncrement;type:bigint;index:idx_upload_pending,priority:2;comment:Metering event auto-increment ID"`
	CostID        string          `gorm:"type:varchar(128);not null;uniqueIndex:uk_cost_id;comment:Unique metering event ID, reusing the existing cost_id generation logic"`
	TenantID      uint            `gorm:"type:bigint unsigned;not null;index:idx_tenant_time,priority:1;comment:Tenant ID"`
	Unit          string          `gorm:"type:varchar(64);not null;comment:Metering unit"`
	Cost          decimal.Decimal `gorm:"type:decimal(30,15);not null;comment:Usage amount"`
	CostTimestamp int64           `gorm:"type:bigint;not null;comment:Metering time as Unix seconds;index:idx_tenant_time,priority:2;index:idx_cost_time_uploaded,priority:1"`
	Labels        Labels          `gorm:"type:json;not null;comment:Business metadata such as clusterID/project/database"`
	SystemLabels  Labels          `gorm:"type:json;comment:System metadata such as dryrun/is_secondary_measure"`
	Status        string          `gorm:"type:varchar(32);not null;default:'to_price';index:idx_status;comment:Pricing status: to_price/pricing/priced/price_failed/price_ignore"`
	UploadedAt    *time.Time      `gorm:"type:timestamp(3);index:idx_cost_time_uploaded,priority:2;index:idx_upload_pending,priority:1;comment:Time when the event was uploaded to S3"`
	Details       string          `gorm:"type:text;comment:Processing details"`
	CreatedAt     time.Time       `gorm:"type:timestamp(3);not null;default:CURRENT_TIMESTAMP(3);comment:Creation time"`
	UpdatedAt     time.Time       `gorm:"type:timestamp(3);not null;default:CURRENT_TIMESTAMP(3);comment:Update time"`
}

func (MeteringEvent) TableName() string {
	return "metering_events"
}

// StripeInvoicePaying represents a paying task that consolidates Stripe invoices
// for a given calendar month and pushes them via Stripe Pay. At most one active
// (pending/running) task per month is enforced by uk_active_paying.
//
// active_flag is a STORED GENERATED column derived from status — see the AGENTS.md
// "gormigrate v2 footgun" rule. The application never writes to it directly; the
// DB computes 1 when status is pending/running, NULL otherwise. NULL values in
// the (month, active_flag) unique index do not collide in MySQL, so completed
// and failed rows do not block a fresh insert.
//
// The composite index idx_pending (status, created_at) supports the worker's
// claim query, which filters on status and orders by created_at ASC.
type StripeInvoicePaying struct {
	ID        uint      `gorm:"primarykey"`
	CreatedAt time.Time `gorm:"index:idx_pending,priority:2"`
	UpdatedAt time.Time
	DeletedAt gorm.DeletedAt `gorm:"index"`
	Month     string         `gorm:"type:varchar(7);not null;uniqueIndex:uk_active_paying,priority:1"`
	Status    string         `gorm:"type:varchar(32);not null;index:idx_pending,priority:1"`
	// STORED GENERATED column: gorm `->` skips INSERT/UPDATE so the
	// application cannot drift the derived value out of sync with
	// status. The full DDL is in the type tag so fresh-DB AutoMigrate
	// emits the column with the GENERATED ALWAYS clause intact; warm-DB
	// re-apply is in migrate_record170.go.
	ActiveFlag   *uint  `gorm:"->;type:tinyint GENERATED ALWAYS AS (CASE WHEN status IN ('pending','running') THEN 1 ELSE NULL END) STORED;uniqueIndex:uk_active_paying,priority:2"`
	CreatedBy    string `gorm:"type:varchar(128);not null"`
	PickedUpAt   *time.Time
	CompletedAt  *time.Time
	Result       *string `gorm:"type:json"`
	ErrorMessage string  `gorm:"type:text"`
}

func (StripeInvoicePaying) TableName() string {
	return "stripe_invoice_paying"
}

// StripeInvoicePushCarryover holds the accumulated sub-threshold amount for a
// (tenant, billing_config) pair across months, so multiple small invoices can be
// merged into one Stripe push once the running total crosses the threshold.
type StripeInvoicePushCarryover struct {
	gorm.Model
	TenantID         uint            `gorm:"not null;uniqueIndex:uk_tenant_bc,priority:1"`
	BillingConfigID  uint            `gorm:"not null;uniqueIndex:uk_tenant_bc,priority:2"`
	Amount           decimal.Decimal `gorm:"type:decimal(30,15);not null;default:0"`
	SourceInvoiceIDs string          `gorm:"type:json;not null"`
	FirstCarryMonth  time.Time       `gorm:"type:date;not null"`
	Version          int             `gorm:"not null;default:0"`
}

func (StripeInvoicePushCarryover) TableName() string {
	return "stripe_invoice_push_carryover"
}

type SavedBillQuery struct {
	gorm.Model
	BillTitle            string
	StartTime            time.Time
	Endtime              time.Time
	LabelsFilter         Labels `gorm:"type:json"`
	ConsumedSourceFilter *string
	InvoiceIDFilter      *uint
}

type UnitCostSummary struct {
	Unit     string `gorm:"not null"`
	Labels   Labels `gorm:"type:json"`
	Internal bool

	CostPrice      decimal.Decimal `gorm:"type:decimal(30, 15)"`
	RawPrice       decimal.Decimal `gorm:"type:decimal(30, 15)"`
	ConsumedSource string          `gorm:"not null"`
	InvoiceID      int             `gorm:"not null;index:user_invoice,priority:2"`
	Cost           int
	TenantID       uint
	// MergedLabelsHistory is populated only when the source row is a Merged
	// UserUnitCost of a label-skipping unit that was created with
	// EnableMergedLabelsHistory on. It lets callers (notably MSP/CPPO
	// SecondaryMeasure) replay each original raw-cost contribution with its own
	// labels instead of the stale merged row's labels.
	MergedLabelsHistory MergedLabelsHistory `gorm:"type:json"`
}

type DateUnitCostSummary struct {
	UnitCostSummary
	Date time.Time
}

type CostsDetailsQuery struct {
	TenantID              uint
	BindedBillingConfigID uint

	BilledDate string
	Unit       string `gorm:"not null"`
	Cost       int64

	Labels         Labels          `gorm:"type:json"`
	ConsumedSource string          `gorm:"not null"`
	CostPrice      decimal.Decimal `gorm:"type:decimal(30, 15)"`
	RawPrice       decimal.Decimal `gorm:"type:decimal(30, 15)"`

	InvoiceID uint
}

// gen:qs
type CommitmentRecords struct {
	ID                    uint `gorm:"primarykey"`
	CreatedAt             time.Time
	Amount                decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Owner                 string          `gorm:"type:varchar(255)"`
	Date                  time.Time
	BindedBillingConfigID uint
	PaymentCode           *string `gorm:"type:varchar(64)"`
}

const (
	SubscriptionStatus_DRAFT     = "draft"
	SubscriptionStatus_PENDING   = "pending"
	SubscriptionStatus_ACCEPTED  = "accepted"
	SubscriptionStatus_REJECTED  = "rejected"
	SubscriptionStatus_SUSPENDED = "suspended"
)

// gen:qs
type Subscription struct {
	gorm.Model
	TenantID      uint   `gorm:"index:user_status,priority:1;index:user_date,priority:1"`
	Status        string `gorm:"type:varchar(255);index:user_status,priority:2"`
	FilePath      string `gorm:"type:varchar(255)"`
	ContractEmail string `gorm:"type:varchar(255)"`
	AcceptedDate  *time.Time
	AcceptedUser  *string `gorm:"type:varchar(255)"`
	RejectedDate  *time.Time
	RejectedUser  *string `gorm:"type:varchar(255)"`
}

const (
	ContractStatusDraft      = "draft"
	ContractStatusDeleted    = "deleted"
	ContractStatusSubmit     = "submit"
	ContractStatusCanceled   = "canceled"
	ContractStatusActive     = "active"
	ContractStatusRejected   = "rejected"
	ContractStatusTerminated = "terminated"
)

const (
	ContractTypeSavingPlanOrderForm = "saving_plan_order_form"
	ContractTypePocAgreement        = "poc_agreement"
	ContractTypePrepaymentOrderForm = "prepayment_order_form"
)

// gen:qs
type Contract struct {
	gorm.Model
	ContractID             string `gorm:"type:varchar(255);index"`
	TenantID               uint   `gorm:"index:user_status,priority:1;index:user_date,priority:1"`
	Status                 string `gorm:"type:varchar(255);index:user_status,priority:2"`
	ContractType           string `gorm:"type:varchar(255)"`
	FilePath               string `gorm:"type:varchar(255)"` // 合同存放的s3路径
	ContactEmail           string `gorm:"type:varchar(255)"`
	LastOperator           uint
	ReasonForLastOperation string `gorm:"type:varchar(255)"`
	BindedBillingConfigID  uint
}

const (
	ContractActionCreate    = "create"
	ContractActionUpdate    = "update"
	ContractActionSubmit    = "submit"
	ContractActionCancel    = "cancel"
	ContractActionAccept    = "accept"
	ContractActionReject    = "reject"
	ContractActionDelete    = "delete"
	ContractActionTerminate = "terminate"
)

// gen:qs
type ContractOperation struct {
	gorm.Model
	ContractID   uint `gorm:"foreignkey:Contract"`
	OperatorID   uint
	Action       string `gorm:"type:varchar(64);index"`
	Reason       string `gorm:"type:varchar(255)"`
	ContactEmail string `gorm:"type:varchar(255)"`
	FilePath     string `gorm:"type:varchar(255)"`
}

const (
	SupportPlanType_FixedPrice      = "fixed_price"
	SupportPlanType_PercentagePrice = "percentage_price"
	SupportPlanType_TieredPrice     = "tiered_price"
	SupportPlanLevel_Basic          = "Basic"
	SupportPlanLevel_Standard       = "Standard"
	SupportPlanLevel_Enterprise     = "Enterprise"
	SupportPlanLevel_Premium        = "Premium"
	SupportPlanLevel_Developer      = "Developer"
)

// gen:qs
type BillingInformation struct {
	TenantID               uint `gorm:"primarykey"`
	CreatedAt              time.Time
	UpdatedAt              time.Time
	CompanyName            string `gorm:"type:varchar(255)"`
	BillingEmail           string `gorm:"type:varchar(255)"`
	CountryCode            string `gorm:"type:varchar(255)"`
	State                  string `gorm:"type:varchar(255)"`
	City                   string `gorm:"type:varchar(255)"`
	Line1                  string `gorm:"type:varchar(255)"`
	Line2                  string `gorm:"type:varchar(255)"`
	PostalCode             string `gorm:"type:varchar(255)"`
	TaxID                  string `gorm:"type:varchar(255);column:tax_vat"`
	TaxIDType              string `gorm:"type:varchar(255)"`
	TaxRateID              string `gorm:"type:varchar(255)"`
	CustomInputCountryName string `gorm:"type:varchar(255)"`
	TaxIDCountryCode       string `gorm:"type:varchar(255)"`
}

// gen:qs
type BillingCompany struct {
	CompanyKey     string `gorm:"primarykey"`
	Name           string `gorm:"type:varchar(255)"`
	PaymentSubCode string `gorm:"type:varchar(255)"`
	StripeTaxID    string `gorm:"type:varchar(255)"`

	UsingDesignatedTaxAddressToCalculateTaxRate bool
	DesignatedTaxAddress                        *BillingAddress `gorm:"json"`

	LocalCurrency                        string `gorm:"varchar(16)"`
	DisplayLocalCurrencyCommentOnInvoice bool
}

// gen:qs
type TenantBillingCompany struct {
	TenantID   uint   `gorm:"primarykey"`
	CompanyKey string `gorm:"type:varchar(255)"`
}

// gen:qs
type CountryCodeCompanyMap struct {
	CountryCode string
	CompanyKey  string
}

// gen:qs
type SupportPlan struct {
	gorm.Model
	FixedChargePrice       decimal.Decimal `gorm:"type:decimal(30, 15)"`
	SupportPricePercentage decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Type                   string          `gorm:"type:varchar(128)"`
	Level                  string          `gorm:"type:varchar(128)"`
	Public                 bool            `gorm:"not null"`
	PackageID              *uint
	TieredPriceRules       TieredPriceRules `gorm:"type:json"`
	IsSupportFreeTrial     bool             `gorm:"default:0"`
}

const (
	BindedSupportPlanStatus_ACTIVE        = "active"
	BindedSupportPlanStatus_TOBEACTIVATED = "to_be_activated"
	BindedSupportPlanStatus_Expired       = "expired"
	BindedSupportPlanStatus_CANCELED      = "canceled"
)

//gen:qs
type BindedSupportPlan struct {
	gorm.Model
	TenantID               uint   `gorm:"index:user_status,priority:1"`
	Status                 string `gorm:"type:varchar(128);index:user_status,priority:2"`
	SupportPlanID          uint   `gorm:"not null;index:support_plan_id"`
	SupportPlan            SupportPlan
	ActiveDate             time.Time
	ExpiredDate            *time.Time
	ClosedDate             *time.Time
	BindedPackageID        *uint
	IsEligibleForFreeTrial bool
}

//gen:qs
type SupportPlanMeteringRecords struct {
	CostID              string `gorm:"primarykey"`
	TenantID            uint
	CostTimeStamp       time.Time
	BindedSupportPlanID uint
	Unit                string `gorm:"type:varchar(128)"`
	Cost                int
	Usage               decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

const (
	CreditsPackageStatus_Active          = "active"
	CreditsPackageStatus_InActive        = "inactive"
	CreditsPackageStatus_BeforeActive    = "before_active"
	CreditsPackageType_General           = "general"
	CreditsPackageUsage_POC              = "poc"
	CreditsPackageUsage_General          = "general"
	CreditsPackageUsage_Marketing        = "marketing"
	CreditsPackageUsage_Refund           = "refund"
	CreditsPackageUsage_Prepayment       = "prepayment"
	CreditsPackageUsage_CustomerSuccess  = "customer_success"
	CreditsPackageUsage_Redeem           = "redeem"
	CreditsPackageUsage_Program          = "program"
	CreditsPackageUsage_Referral         = "referral"
	CreditsPackageUsage_Rerun            = "rerun"
	CreditsPackageClosedReason_Expired   = "expired"
	CreditsPackageClosedReason_Exhausted = "exhausted"
	CreditsPackageClosedReason_Reclaimed = "reclaimed"
)

//gen:qs
type CreditsPackage struct {
	gorm.Model
	TenantID              uint   `gorm:"index:user_status,priority:1"`
	Status                string `gorm:"type:varchar(128);index:user_status,priority:2"`
	CreditsCode           string `gorm:"type:varchar(128);uniqueIndex"`
	StartDate             time.Time
	ExpiredDate           time.Time
	ClosedDate            *time.Time
	BindedPackageID       *uint
	BindedPackage         *BindedPackage  `gorm:"foreignKey:ID;references:binded_package_id"`
	TotalCredits          decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Type                  string          `gorm:"type:varchar(128)"` // Deprecated field
	Usage                 string          `gorm:"type:varchar(128)"`
	ClosedReason          string          `gorm:"type:varchar(128)"`
	Note                  string          `gorm:"type:varchar(128)"`
	DiscountEnabled       bool
	UnitFilters           []*UnitFilter `gorm:"many2many:credit_type_credit_package;joinReferences:credit_type_id"`
	Internal              bool          `gorm:"default:0;index"`
	BindedBillingConfigID uint
	UsageExtend           string             `gorm:"type:varchar(128)"`
	CopyUnitFilterArgs    CopyUnitFilterArgs `type:"json"`
}

var NoLimitCostQuota = decimal.NewFromInt(-1)

//gen:qs
type CostQuota struct {
	TenantID       uint `gorm:"primarykey"`
	CreatedAt      time.Time
	UpdatedAt      time.Time
	CostQuotaLimit decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

//gen:qs
type CostQuotaCreditRiskLevel struct {
	CreditRiskLevelID uint `gorm:"primarykey"`
	CreatedAt         time.Time
	UpdatedAt         time.Time
	CostQuotaLimit    decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

//gen:qs
type CreditRiskLevel struct {
	gorm.Model
	Name string `gorm:"type:varchar(128)"`
}

//gen:qs
type TenantCreditRiskLevel struct {
	TenantID          uint `gorm:"primarykey"`
	CreatedAt         time.Time
	UpdatedAt         time.Time
	CreditRiskLevelID uint `gorm:"index"`
}

//gen:qs
type AuditRecord struct {
	ID        uint      `gorm:"primarykey"`
	CreatedAt time.Time `gorm:"index:idx_create_at_and_tenant_id,priority:1"`
	TenantID  uint      `gorm:"index:idx_create_at_and_tenant_id,priority:2"`
	UserID    uint      `gorm:"index"`
	Action    string    `gorm:"type:varchar(64)"`
	ExtraInfo string    `gorm:"type:longtext"`
	UserEmail string    `gorm:"type:varchar(255)"`
}

//gen:qs
type CategoryItemShow struct {
	Category          string   `gorm:"type:varchar(255)"`
	Labels            Labels   `gorm:"type:json"`
	LabelKey          string   `gorm:"type:varchar(255)"`
	ShowServicePrefix string   `gorm:"type:varchar(255)"`
	ShowCategoryName  string   `gorm:"type:varchar(255)"`
	UseCateGory       bool     `gorm:"not null"`
	ShowKindName      string   `gorm:"type:varchar(255)"`
	OrderMap          OrderMap `gorm:"type:json"`
	EditionCode       string   `gorm:"type:varchar(256)"`
	CommodityCode     string   `gorm:"type:varchar(64)"`
	MeasureUnit       string   `gorm:"type:varchar(255)"`
}

const (
	AuditRecordActionChangeBindedDiscount = "ChangeBindedDiscount"
	AuditRecordActionCreateCredit         = "CreateCredit"
	AuditRecordActionReclaimCredit        = "ReclaimCredit"
	AuditRecordActionConfirmInvoice       = "ConfirmInvoice"
	AuditRecordActionSuspendInvoice       = "SuspendInvoice"
)

// gen:qs
type CreditCardValidateRefundRecord struct {
	gorm.Model
	TenantID        uint            `gorm:"not null"`
	PaymentIntentID string          `gorm:"uniqueIndex,not null"`
	RefundPrice     decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Status          string          `gorm:"not null"`
	RetryCount      uint
	RefundID        string
}

// enum of CreditCardValidateRefundRecord.Status
const (
	CreditCardValidateRefundStatus_Init     = "refund_init"
	CreditCardValidateRefundStatus_Success  = "refund_success"
	CreditCardValidateRefundStatus_Fail     = "refund_fail"
	CreditCardValidateCancelStatus_Init     = "cancel_init"
	CreditCardValidateCancelStatus_Success  = "cancel_success"
	CreditCardValidateCancelStatus_Fail     = "cancel_fail"
	CreditCardValidateCancelStatus_NoAction = "no_action"
)

const (
	TenantCloseReason_Risky     = "risky"
	TenantCloseReason_Unpayable = "unpayable"
)

//gen:qs
type TenantClosePolicy struct {
	gorm.Model
	CreditRiskLevelID uint        `gorm:"index;uniqueIndex:uidx_reason_risk_level,priority:2;default:0"`
	Reason            string      `gorm:"type:varchar(128);uniqueIndex:uidx_reason_risk_level,priority:1;default:risky"`
	ClosePolicy       ClosePolicy `gorm:"type:json"`
}

func (*TenantClosePolicy) TableName() string {
	return "credit_risk_close_policies"
}

const (
	TenantCloseStatus_NOTSTARTED = "not_started"
	TenantCloseStatus_RUNNING    = "running"
	TenantCloseStatus_DONE       = "done"
	TenantCloseStatus_CANCELING  = "canceling"
	TenantCloseStatus_CANCELED   = "canceled"
)

//gen:qs
type TenantCloseRecord struct {
	gorm.Model
	TenantID           uint `gorm:"index:user_status,priority:1"`
	CreditRiskLevelID  uint
	Reason             string             `gorm:"type:varchar(128)"`
	Status             string             `gorm:"type:varchar(64);index:user_status,priority:2"`
	CloseActionsStatus CloseActionsStatus `gorm:"type:json"`
	ClosePolicy        ClosePolicy        `gorm:"type:json"`
	StartedAt          time.Time
	ClosedAt           *time.Time
}

func (*TenantCloseRecord) TableName() string {
	return "tenant_credit_risk_close_records"
}

const (
	TenantPlan_NewUser  = "new_user"
	TenantPlan_DevTier  = "free_trial"
	TenantPlan_POC      = "poc"
	TenantPlan_OnDemand = "on_demand"
	TenantPlan_Closing  = "closing"
)

const (
	TenantPlanStatus_NotStarted = "not_started"
	TenantPlanStatus_Active     = "active"
	TenantPlanStatus_Closing    = "closing"
	TenantPlanStatus_Closed     = "closed"
)

//gen:qs
type TenantPlan struct {
	gorm.Model
	TenantID              uint   `gorm:"index:user_status,priority:1;uniqueIndex:user_plan,priority:1"`
	Status                string `gorm:"type:varchar(128);index:user_status,priority:2"`
	Plan                  string `gorm:"type:varchar(128);uniqueIndex:user_plan,priority:2"`
	BindedPackageID       uint
	StartedAt             *time.Time
	ClosedAt              *time.Time
	BindedBillingConfigID uint `gorm:"uniqueIndex:user_plan,priority:3"`
}

type ModifierAndCreator struct {
	Modifier string `gorm:"type:varchar(64)"`
	Creator  string `gorm:"type:varchar(64)"`
	Describe string `gorm:"type:varchar(256)"`
}

//gen:qs
type Commodity struct {
	gorm.Model
	CommodityCode     string `gorm:"type:varchar(64);uniqueIndex;not null"`
	CommodityShowName string `gorm:"type:varchar(256);not null"`
	CommodityType     string `gorm:"type:varchar(64);not null"`
	Owner             string `gorm:"type:varchar(64);not null"`
	ModifierAndCreator
}
type DimensionValue forms.DimensionValue
type EditionDimensionValue forms.EditionDimensionValue

//gen:qs
type CommodityDimension struct {
	gorm.Model
	DimensionCode     string         `gorm:"type:varchar(256);uniqueIndex;not null"`
	CommodityCode     string         `gorm:"type:varchar(64)"`
	DimensionShowName string         `gorm:"type:varchar(256)"`
	DimensionValue    DimensionValue `gorm:"type:json"`
	IsShare           bool
	IsCharge          bool
	ModifierAndCreator
}

//gen:qs
type CommodityEdition struct {
	gorm.Model
	EditionCode           string                `gorm:"type:varchar(256);uniqueIndex;not null"`
	CommodityCode         string                `gorm:"type:varchar(64)"`
	EditionShowName       string                `gorm:"type:varchar(256)"`
	EditionDimensionValue EditionDimensionValue `gorm:"type:json"`
	ModifierAndCreator
}

//gen:qs
type SkuTemplate struct {
	gorm.Model
	EditionCode   string         `gorm:"type:varchar(256);uniqueIndex;not null"`
	CommodityCode string         `gorm:"type:varchar(64);uniqueIndex;not null"`
	Template      DimensionValue `gorm:"type:json"`
	SkuCount      uint
	ModifierAndCreator
}

//gen:qs
type VersionSku struct {
	gorm.Model
	VersionId uint
	SkuKey    string `gorm:"type:varchar(64);not null"`
	Unit      Unit   `gorm:"type:json"`
}

const SpendLimitInterval_Monthly = "monthly"

const (
	SpendLimitRangeType_Tenant   = "tenant"
	SpendLimitRangeType_Cluster  = "cluster"
	SpendLimitRangeType_Project  = "project"
	SpendLimitRangeType_Customer = "customer"
)

const (
	SpendLimitCostType_Total   = "total"
	SpendLimitCostType_Invoice = "invoice"
	SpendLimitCostType_Credits = "credits"
)

const (
	SpendLimitStatus_Excessive  = "excessive"
	SpendLimitStatus_Fit        = "fit"
	SpendLimitStatus_Deprecated = "deprecated"
	SpendLimitStatus_Closing    = "closing"
	SpendLimitStatus_Recovered  = "recovered"
)

const (
	SpendLimitBizType_Normal            = "budget_alert"
	SpendLimitBizType_ServerlessCluster = "serverless_cluster"
)

//gen:qs
type SpendLimitSetting struct {
	gorm.Model
	TenantID     uint            `gorm:"index;not null;index:free_serverless_cluster_index,priority:1"`
	Interval     string          `gorm:"type:varchar(32);not null;default:'monthly"`
	CostType     string          `gorm:"type:varchar(32);not null;default:'total"`
	RangeType    string          `gorm:"type:varchar(32);not null;default:'tenant"`
	RangeID      uint            `gorm:"not null;index;"`
	Limit        decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	Status       string          `gorm:"type:varchar(32);not null;default:'fit'" `
	BizType      string          `gorm:"type:varchar(32);index:serverless_cluster,priority:1;"`
	BizData      string          `gorm:"type:longtext"`
	BizUniqueKey *string         `gorm:"type:varchar(128);uniqueIndex"`
	WaterMark    decimal.Decimal `gorm:"type:decimal(30, 15);default:0"`
	Name         string          `gorm:"type:varchar(128)"`

	HasServerlessFreeCredits bool `gorm:"->;type:bool GENERATED ALWAYS AS (IF(JSON_VALID(biz_data) AND biz_type='serverless_cluster', JSON_UNQUOTE(biz_data->'$.free_credits') > 0, FALSE)) STORED;index:free_serverless_cluster_index,priority:2"`
}

const (
	SpendLimitAlertThresholdAction_SendingEmail             = "email"
	SpendLimitAlertThresholdAction_SendingInAppNotification = "email"
)

//gen:qs
type SpendLimitAlertThreshold struct {
	gorm.Model
	BizType         string          `gorm:"index;not null"`
	Proportion      decimal.Decimal `gorm:"type:decimal(30, 15);not null"`
	TenantID        uint            `gorm:"default:0"`
	SpendingLimitID uint            `gorm:"index;default:0;not null"`
}

const RechargePlanCycle_Month = "month"

const RechargeCreditsPlanSource_Web = "web"
const RechargeCreditsPlanSource_System = "system"

//gen:qs
type RechargeCreditsPlan struct {
	gorm.Model
	Value       decimal.Decimal         `gorm:"type:decimal(30, 15)"`
	Usage       string                  `gorm:"type:varchar(128)"`
	Note        string                  `gorm:"type:varchar(128)"`
	Internal    bool                    `gorm:"default:0"`
	Rule        RechargeCreditsPlanRule `gorm:"type:json"`
	Cycle       string                  `gorm:"type:varchar(32)"`
	Trigger     *string                 `gorm:"type:varchar(64);index"`
	Duration    time.Duration
	StartDate   time.Time `gorm:"index:idx_start_expired_date,priority:1"`
	ExpiredDate time.Time `gorm:"index;index:idx_start_expired_date,priority:2"`
	ClosedAt    *time.Time
	Source      string `gorm:"type:varchar(32);default:system;not null;index"`

	// deprecated
	UnitFilters []*UnitFilter `gorm:"many2many:credit_type_credit_cycle_recharge_credits_plan;joinReferences:credit_type_id"`
}

//gen:qs
type TenantRechargeCreditsPlan struct {
	gorm.Model
	TenantID           uint `gorm:"uniqueIndex:tenant_recharge_plan,priority:1;idx_tenant_recharge_date,priority:1;index:idx_tenant_expired_date,priority:1"`
	MainPlanID         uint `gorm:"index;uniqueIndex:tenant_recharge_plan,priority:2"`
	RechargeTimes      uint
	NextCycleStartTime time.Time `gorm:"index;index:idx_tenant_recharge_date,priority:2"`
	StartDate          time.Time
	ExpiredDate        time.Time  `gorm:"index:idx_tenant_expired_date,priority:2"`
	ClosedAt           *time.Time `gorm:"index;index:idx_tenant_recharge_date,priority:3;index:idx_tenant_expired_date,priority:3"`
	FilterIDs          string     `gorm:"type:varchar(128);uniqueIndex:tenant_recharge_plan,priority:3"`

	DeletedUniqueKeyMark string `gorm:"type:varchar(36);default:'alive';uniqueIndex:tenant_recharge_plan,priority:4"`

	UnitFilters []*UnitFilter `gorm:"many2many:unit_filter_tenant_recharge_credits_plan"`
}

//gen:qs
type RechargeCreditsPlanRecord struct {
	ID               uint `gorm:"primarykey"`
	UpdatedAt        time.Time
	CreatedAt        time.Time      `gorm:"index:idx_tenant_plan_created_at,priority:2"`
	DeletedAt        gorm.DeletedAt `gorm:"index"`
	TenantID         uint           `gorm:"index"`
	MainPlanID       uint           `gorm:"index"`
	TenantPlanID     uint           `gorm:"index:idx_tenant_plan_created_at,priority:1"`
	CreditsPackageID uint           `gorm:"index"`

	CreditsPackage CreditsPackage            `gorm:"foreignKey:ID;references:CreditsPackageID"`
	MainPlan       RechargeCreditsPlan       `gorm:"foreignKey:ID;references:MainPlanID"`
	TenantPlan     TenantRechargeCreditsPlan `gorm:"foreignKey:ID;references:TenantID"`
}

//gen:qs
type Discount struct {
	gorm.Model
	Code                  string          `gorm:"type:varchar(128);"`
	TenantID              uint            `gorm:"index;index:user_active,priority:2"`
	Value                 decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Version               uint
	StartedAt             time.Time     `gorm:"index:validity_period,priority:3"`
	ExpiredAt             time.Time     `gorm:"index:validity_period,priority:1"`
	ClosedAt              *time.Time    `gorm:"index:validity_period,priority:2"`
	UnitFilters           []*UnitFilter `gorm:"many2many:discount_unit_filters;"`
	Status                string        `gorm:"type:varchar(32)"`
	Active                string        `gorm:"type:varchar(32);index:user_active,priority:1;index:inactive_time,priority:2;default:normal"`
	InactiveTime          *time.Time    `gorm:"index:inactive_time,priority:1"`
	Usage                 string
	Description           string
	Note                  string `gorm:"type:longtext"`
	OldDiscountUniqueMark *uint  `gorm:"uniqueIndex"`
	BindedBillingConfigID uint
	CopyUnitFilterArgs    CopyUnitFilterArgs `type:"json"`
}

func (*Discount) TableName() string {
	return "tenant_discounts"
}

//gen:qs
type DiscountVersion struct {
	gorm.Model
	DiscountID uint `gorm:"index"`
	TenantID   uint
	Value      decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Reason     string
	OperatorID uint
}

func (*DiscountVersion) TableName() string {
	return "tenant_discount_versions"
}

//gen:qs
type TieredBilling struct {
	gorm.Model
	TenantID    uint `gorm:"index"`
	Labels      Labels
	Interval    string
	TargetValue decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Priority    int
	Action      string
	ClusterID   string `gorm:"->;type:VARCHAR(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(CAST(labels AS CHAR CHARACTER SET utf8mb4), '$.clusterID'))) STORED;index:idx_cluster_id"`
}

//gen:qs
type TieredBillingAccumulator struct {
	gorm.Model
	TieredBillingID uint            `gorm:"uniqueIndex:unique_tier_id_cost_time,priority:1"`
	CostTimeKey     int64           `gorm:"uniqueIndex:unique_tier_id_cost_time,priority:2"`
	CumulativeValue decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

//gen:qs
type WorkerRunningRecord struct {
	gorm.Model
	TenantID     uint   `gorm:"index"`
	WorkerName   string `gorm:"type:varchar(256)"`
	WorkerKey    string `gorm:"type:varchar(512);uniqueIndex;not null"`
	RunningParam string `gorm:"type:longtext"`
}

const (
	BillingConfigStatus_Active    = "Active"
	BillingConfigStatus_Closing   = "Closing"
	BillingConfigStatus_Closed    = "Closed"
	BillingConfigStatus_Scheduled = "Scheduled"
	BillingConfigStatus_Managing  = "Managing"

	BillingConfigType_DirectCustomer   = "DirectCustomer"
	BillingConfigType_MSPCustomer      = "MSPCustomer"
	BillingConfigType_MSP              = "MSP"
	BillingConfigType_ResalePartner    = "ResalePartner"
	BillingConfigType_ResaleCustomer   = "ResaleCustomer"
	BillingConfigType_Alicloud         = "Alicloud"
	BillingConfigType_AlicloudCustomer = "AlicloudCustomer"

	BillingConfigType_AWSCPPOReseller                    = "CPPOReseller"
	BillingConfigType_AWSCPPOCustomer                    = "CPPOCustomer"
	BillingConfigType_MarketplaceReseller                = "MarketplaceReseller"
	BillingConfigType_MarketplaceResellerManagedCustomer = "MarketplaceResellerManagedCustomer"
)

//gen:qs
type BillingConfig struct {
	gorm.Model
	TenantID        uint `gorm:"index;user_status,priority:1;uniqueIndex:uidx_type_tenant_id_managed_tenant_id,priority:2"`
	StartActiveTime time.Time
	EndActiveTime   *time.Time
	ClosedTime      *time.Time
	Type            string `gorm:"type:varchar(64);uniqueIndex:uidx_type_tenant_id_managed_tenant_id,priority:1"`
	Status          string `gorm:"type:varchar(64);index:user_status,priority:2"`
	ManagedTenantID uint   `gorm:"index;default:0;uniqueIndex:uidx_type_tenant_id_managed_tenant_id,priority:3"`
	NeedUniqueMark  *bool  `gorm:"default:1;uniqueIndex:uidx_type_tenant_id_managed_tenant_id,priority:4"`

	BindSupportPlanOnEnabled bool `gorm:"default:false"`
	SupportPlanID            *uint
	SupportPlanExpiredDate   *time.Time
}

//gen:qs
type PartnerConfigs struct {
	gorm.Model
	PartnerID           uint `gorm:"uniqueIndex:uidx_partner_id_customer_id,priority:2;index"`
	CustomerID          uint `gorm:"uniqueIndex:uidx_partner_id_customer_id,priority:1;default:0"`
	EnableCustomerBills bool
}

//gen:qs
type InvoiceAppliedFX struct {
	gorm.Model
	Date   time.Time       `gorm:"index:idx_date_currency,priority:3"`
	Base   string          `gorm:"type:varchar(12);index:idx_date_currency,priority:2"`
	Target string          `gorm:"type:varchar(12);index:idx_date_currency,priority:1"`
	Value  decimal.Decimal `gorm:"type:decimal(10, 6)"`
}

// gen:qs
type AliyunSubscriptionManagement struct {
	gorm.Model
	AliUid       string `gorm:"type:VARCHAR(255);index;not null;column:ali_uid;"`
	BizId        string `gorm:"type:VARCHAR(255);uniqueIndex;not null;column:biz_id;"`
	InTrial      bool   `gorm:"not null;column:in_trial;"`
	IsSubscribed bool   `gorm:"not null;column:is_subscribed;"`
	ProductCode  string `gorm:"type:VARCHAR(255);column:product_code;"`
	IsThirdParty bool   `gorm:"not null;default:false;column:is_third_party;"`
}

// gen:qs
type AWSSubscriptionManagement struct {
	gorm.Model
	ProductCode        string `gorm:"type:VARCHAR(255);not null;column:product_code;"`
	CustomerIdentifier string `gorm:"type:VARCHAR(255);not null;column:customer_identifier;"`
	InTrial            bool   `gorm:"not null;column:in_trial;"`
	IsSubscribed       bool   `gorm:"not null;column:is_subscribed;"`
}

type StringSlice []string

//gen:qs
type ARTableRecord struct {
	gorm.Model
	TenantID              uint `gorm:"uniqueIndex:uidx_ar_record,priority:1"`
	PartnerID             string
	PaymentCode           string `gorm:"type:varchar(64)"`
	PaymentType           string `gorm:"type:varchar(32)"`
	BillMonth             string `gorm:"type:varchar(7);uniqueIndex:uidx_ar_record,priority:2"`
	StartDate             string
	EndDate               string
	BindedBillingConfigID uint `gorm:"uniqueIndex:uidx_ar_record,priority:3"`
	IsEstimated           bool `gorm:"uniqueIndex:uidx_ar_record,priority:4"`

	StripeAmount               StringSlice `gorm:"type:json"`
	StripeTaxRate              StringSlice `gorm:"type:json"`
	StripeAmountIncludingTax   StringSlice `gorm:"type:json"`
	StripeInvoiceNumber        StringSlice `gorm:"type:json"`
	WireTransferAmount         string
	MarketplaceAmount          string
	PrepaymentCredits          string
	TotalAmount                string
	InvoiceAmount              string
	StripeInvoice              StringSlice `gorm:"type:json"`
	StripeInvoicePaid          StringSlice `gorm:"type:json"`
	StripeInvoiceFinalizedDate StringSlice `gorm:"type:json"`
	StripeInvoicePaidDate      StringSlice `gorm:"type:json"`

	RawServerlessAmount string
	RawDedicatedAmount  string
	RawStarterAmount    string
	RawEssentialAmount  string
	RawSupportAmount    string
	RawPremiumAmount    string
	RawLakeAmount       string
	RawBYOCAmount       string

	RawServerlessProgramCreditsAmount string
	RawDedicatedProgramCreditsAmount  string
	RawStarterProgramCreditsAmount    string
	RawEssentialProgramCreditsAmount  string
	RawSupportProgramCreditsAmount    string
	RawPremiumProgramCreditsAmount    string
	RawLakeProgramCreditsAmount       string
	RawBYOCProgramCreditsAmount       string

	RawServerlessRedeemCreditsAmount string
	RawDedicatedRedeemCreditsAmount  string
	RawStarterRedeemCreditsAmount    string
	RawEssentialRedeemCreditsAmount  string
	RawSupportRedeemCreditsAmount    string
	RawPremiumRedeemCreditsAmount    string
	RawLakeRedeemCreditsAmount       string
	RawBYOCRedeemCreditsAmount       string

	RawServerlessCustomerSuccessAmount string
	RawDedicatedCustomerSuccessAmount  string
	RawStarterCustomerSuccessAmount    string
	RawEssentialCustomerSuccessAmount  string
	RawSupportCustomerSuccessAmount    string
	RawPremiumCustomerSuccessAmount    string
	RawLakeCustomerSuccessAmount       string
	RawBYOCCustomerSuccessAmount       string

	ChannelLossAmount                string
	ServerlessAllocatedLoss          string
	DedicatedAllocatedLoss           string
	StarterAllocatedLoss             string
	EssentialAllocatedLoss           string
	SupportAllocatedLoss             string
	PremiumAllocatedLoss             string
	LakeAllocatedLoss                string
	BYOCAllocatedLoss                string
	CPPODiscountableProductAmount    string
	CPPONonDiscountableProductAmount string

	DedicatedAccrualAmount string
	SupportAccrualAmount   string
}

var Models = []interface{}{
	&Unit{},
	&Package{},
	&PackageVersion{},
	&BindedPackage{},
	&UserUnitCost{},
	&RawUserUnitCost{},
	&PaymentMethod{},
	&PaymentMethodActiveRecord{},
	&Invoice{},
	&BillingSnapshotInputBundle{},
	&BillingSnapshotRun{},
	&BillingSnapshotSettlementFact{},
	&BillingSnapshotActiveRun{},
	&BillingSnapshotInvoiceLink{},
	&BillingSnapshotApplyRecord{},
	&MeteringEvent{},
	&StripeInvoicePaying{},
	&StripeInvoicePushCarryover{},
	&InvoiceAppliedFX{},
	&CreditsChargeRecord{},
	&BindedDiscountChangeRecord{},
	&CommitmentRecords{},
	&Subscription{},
	&BillingInformation{},
	&BillingCompany{},
	&TenantBillingCompany{},
	&CountryCodeCompanyMap{},
	&SupportPlan{},
	&BindedSupportPlan{},
	&SupportPlanMeteringRecords{},
	&CreditsPackage{},
	&CostQuota{},
	&CostQuotaCreditRiskLevel{},
	&CreditRiskLevel{},
	&TenantCreditRiskLevel{},
	&AuditRecord{},
	&Contract{},
	&ContractOperation{},
	&CategoryItemShow{},
	&CreditCardValidateRefundRecord{},
	&TenantClosePolicy{},
	&TenantCloseRecord{},
	&TenantPlan{},
	&UnitFilter{},
	&UnitFilterSelection{},
	&Commodity{},
	&CommodityDimension{},
	&CommodityEdition{},
	&SkuTemplate{},
	&VersionSku{},
	&SpendLimitSetting{},
	&SpendLimitAlertThreshold{},
	&RechargeCreditsPlan{},
	&TenantRechargeCreditsPlan{},
	&RechargeCreditsPlanRecord{},
	&Discount{},
	&DiscountVersion{},
	&TieredBilling{},
	&TieredBillingAccumulator{},
	&WorkerRunningRecord{},
	&BillingConfig{},
	&PartnerConfigs{},
	&SKUGroups{},
	&AliyunSubscriptionManagement{},
	&AWSSubscriptionManagement{},
	&ReferralCampaign{},
	&ReferralCode{},
	&ReferralEvent{},
	&MeteringEvent{},
	&ARTableRecord{},
	&MeteringEvent{},
	&PricingEvent{},
	&PricingComparisonLog{},
	&TenantUnit{},
	&TenantUsage{},
	&FreeCapAccumulator{},
	&WorkerMeteringRecords{},
}

// Referral system constants
const (
	ReferralCampaignStatus_Active = "active"
	ReferralCampaignStatus_Paused = "paused"
	ReferralCampaignStatus_Ended  = "ended"

	ReferralEventType_Signup               = "signup"
	ReferralEventType_BindCard             = "bindcard"
	ReferralEventType_FirstUse             = "first_use"
	ReferralEventType_ContinuousUse        = "continuous_use"
	ReferralEventType_RefereeContinuousUse = "referee_continuous_use"
	ReferralEventType_RefereeSignup        = "referee_signup"
	ReferralEventType_RefereeBindCard      = "referee_bindcard"
	ReferralEventType_CommunityPayment     = "community_payment"

	ReferralSkipReason_BudgetExhausted = "budget_exhausted"
)

//gen:qs
type ReferralCampaign struct {
	gorm.Model
	Name              string          `gorm:"type:varchar(256);not null"`
	Status            string          `gorm:"type:varchar(32);not null;default:active;index"`
	TotalBudget       decimal.Decimal `gorm:"type:decimal(30,15);not null"`
	UsedBudget        decimal.Decimal `gorm:"type:decimal(30,15);not null;default:0"`
	CreatedBy         string          `gorm:"type:varchar(255)"`
	Note              string          `gorm:"type:varchar(512)"`
	IsWebsiteCampaign bool            `gorm:"not null;default:false"`
}

//gen:qs
type ReferralCode struct {
	gorm.Model
	TenantID      uint            `gorm:"uniqueIndex:uidx_tenant_campaign,priority:1;not null"`
	CampaignID    uint            `gorm:"uniqueIndex:uidx_tenant_campaign,priority:2;not null;index"`
	Code          string          `gorm:"type:varchar(64);uniqueIndex;not null"`
	ReferralCount int             `gorm:"not null;default:0"`
	TotalEarned   decimal.Decimal `gorm:"type:decimal(30,15);not null;default:0"`
}

//gen:qs
type ReferralEvent struct {
	gorm.Model
	ReferralCodeID   uint            `gorm:"index;not null"`
	ReferrerTenantID uint            `gorm:"index;not null"`
	RefereeTenantID  uint            `gorm:"index;uniqueIndex:idx_referee_type"`
	EventType        string          `gorm:"type:varchar(64);not null;uniqueIndex:idx_referee_type"`
	Rewarded         bool            `gorm:"not null;default:false"`
	RewardAmount     decimal.Decimal `gorm:"type:decimal(30,15)"`
	CreditsPackageID uint
	CampaignID       uint
	SkipReason       string `gorm:"type:varchar(128)"`
}
