package entity

import (
	"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)"`
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

type PackageUnits forms.UnitPrices

type Labels map[string]string

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

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 BindedPackage struct {
	gorm.Model
	TenantID               uint             `gorm:"index:user_status,priority:1;index:user_package,priority:1"`
	PackageID              uint             `gorm:"index:package_id;index:user_package,priority:2"`
	BindedDiscount         *decimal.Decimal `gorm:"type:decimal(30, 15)"`
	BindedDiscountRecordID *uint
	RemainingCredits       decimal.Decimal `gorm:"type:decimal(30, 15)"`
	Status                 string          `gorm:"not null;index:user_status,priority:2"`
}

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"
	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:user_timestamp,priority:2;index:timestamp"`
	TenantID      uint   `gorm:"index:user_invoice,priority:1;index:user_timestamp,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:"not null"`
	ConsumedSource               string `gorm:"not null"`
	InvoiceID                    int    `gorm:"not null;index:user_invoice,priority:2"`
	Type                         string `gorm:"not null;default:'raw'"`
	CreatedAt                    time.Time
	UpdatedAt                    time.Time
}

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

const (
	PaymentType_AWS    = "aws"
	PaymentType_GCP    = "gcp"
	PaymentType_STRIPE = "stripe"
)

// gen:qs
type PaymentMethod struct {
	gorm.Model
	TenantID    uint   `gorm:"uniqueIndex:user_id,priority:1"`
	PaymentType string `gorm:"not null"`
	PaymentCode string `gorm:"not null"`
	Payable     bool   `gorm:"not null,default:false"`
}

const (
	InvoiceStatus_INITED    = "inited"
	InvoiceStatus_GENERATED = "generated"
	InvoiceStatus_CONFIRMED = "confirmed"
	InvoiceStatus_SENT      = "sent"
	InvoiceStatus_PAID      = "paid" // for now, we do not use this status
)

const (
	InvoiceType_MONTHLY    = "monthly"
	InvoiceType_HOURLY     = "hourly"
	InvoiceType_COMMITMENT = "commitment"
)

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

// 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:user_date,priority:2"`
	CostPrice   decimal.Decimal `gorm:"type:decimal(30, 15)"`
	RawPrice    decimal.Decimal `gorm:"type:decimal(30, 15)"`
}

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

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"`

	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"`
}

// 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
}

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)"`
}

var Models = []interface{}{
	&Unit{},
	&Package{},
	&PackageVersion{},
	&BindedPackage{},
	&UserUnitCost{},
	&PaymentMethod{},
	&Invoice{},
	&CreditsChargeRecord{},
	&BindedDiscountChangeRecord{},
	&CommitmentRecords{},
	&Subscription{},
}
