package facade

import (
	"context"
	"time"

	"github.com/shopspring/decimal"
	"github.com/tidbcloud/pkgv2/bizerr"
)

type Expression struct {
	Label    string `json:"label"`
	Operator string `json:"operator"`
	Value    string `json:"value"`
}

type PricingInfo struct {
	Discount    *string                  `json:"discount"` // nil if no discount
	Expressions []Expression             `json:"expression_labels"`
	Labels      map[string]string        `json:"labels"`
	UnitPrices  map[string]*BillingPrice `json:"unit_prices"`
}

type BillingPrice struct {
	Unit                    string `json:"unit"`
	Price                   string `json:"price"`
	MultipleFactorLabel     string `json:"multiple_factor_label"`
	MultipleFactorThreshold int64  `json:"multiple_factor_threshold"`
}

type BillingPackage struct {
	ID         uint64                   `json:"package_id"`
	UnitPrices map[string]*BillingPrice `json:"unit_prices"` // key:sku
	Version    int                      `json:"version"`
	// ExtraPricingInfos []PricingInfo            `json:"extra_pricing_infos"`
}

type BillingPackage4Update struct {
	ID                uint64                   `json:"package_id"`
	UnitPrices        map[string]*BillingPrice `json:"unit_prices"` // key:sku
	ExtraPricingInfos []PricingInfo            `json:"extra_pricing_infos"`
}

type BillingUnit struct {
	Category    string `json:"category"`
	Description string `json:"description"`
	SkuName     string `json:"unit"`
}

type BillingReport struct {
	CostID        string
	TenantID      uint64
	Sku           string
	Cost          int64
	CostTimeStamp int64
	Labels        map[string]string
}

type BillingCharge struct {
	RawPrice decimal.Decimal
}

type StripePaymentMethod struct {
	ID string `json:"id"`
	//	Card           *Card           `json:"card"`
	//	BillingDetails *BillingDetails `json:"billing_details"`
}

type TenantPlanInfo struct {
	TenantID      uint   `json:"tenant_id"`
	EffectivePlan string `json:"effective_plan"`
	/*
		DevTierPlanInfo   *PlanInfo       `json:"dev_tier_plan_info"`
		POCPlanInfo       *PlanInfo       `json:"poc_plan_info"`
		OnDemandPlanInfo  *PlanInfo       `json:"ondemand_plan_info"`
		EffectiveDiscount decimal.Decimal `json:"effective_discount"`
	*/
	POCCredits decimal.Decimal `json:"poc_credits"`
}

type SpendLimitSetting struct {
	SpendLimitSettingID uint64          `json:"spendlimit_setting_id"`
	TenantID            uint64          `json:"tenant_id"`
	Interval            string          `json:"interval"`
	CostType            string          `json:"cost_type"`
	RangeType           string          `json:"range_type"`
	RangeID             uint64          `json:"range_id"`
	Limit               decimal.Decimal `json:"limit"`
	Status              string          `json:"status"`
	BizType             string          `json:"biz_type"`
	BizData             string          `json:"biz_data"`
}

type SpendLimitInfo struct {
	SpendLimitSetting
	Costs     decimal.Decimal `json:"costs"`
	StartDate time.Time       `json:"start_date"`
}

type ServerlessSpendLimitBizData struct {
	FreeCredits        decimal.Decimal `json:"free_credits"` // For Set FreeCredit
	InvoiceLimit       decimal.Decimal `json:"invoice_limit"`
	RemainingCredits   decimal.Decimal `json:"remaining_credits"`
	ActiveTotalCredits decimal.Decimal `json:"active_total_credits"` // For Query FreeCredit
	RechargePlanID     uint            `json:"recharge_plan_id"`     // For regional FreeCredit
}

type CostExplorerInternalQuery struct {
	StartDate  time.Time         `json:"start_date"`
	EndDate    time.Time         `json:"end_date"` /* eg. 2023-05-24T00:00:00Z */
	Labels     map[string]string `json:"labels"`   /* eg. {"clusterID": "3299134"} */
	Units      []string          `json:"units"`    /* sku eg. serverless-request-unit:aws:us-east-1 */
	Internal   bool              `json:"internal"` /* false: SpendLimit cost; true: FreeCredit cost. just for v1 */
	IgnoreBeta bool              `json:"ignore_beta"`
}

type CostExplorerResult struct {
	BindedInvoiceCosts   Costs `json:"binded_invoice_costs"`
	UnbindedInvoiceCosts Costs `json:"unbind_invoice_costs"`
	InvoiceCostsCosts    Costs `json:"invoice_costs"`
	CreditsCosts         Costs `json:"credits_costs"`
	TotalCosts           Costs `json:"total_costs"`
}

type Costs struct {
	CostPrice     decimal.Decimal `json:"cost_price"`
	RawPrice      decimal.Decimal `json:"raw_price"`
	DiscountPrice decimal.Decimal `json:"discount_price"`
	/* for v2 */
	CategoryCosts
	LabelsUnitsCosts       map[string]LabelCosts        `json:"label_unit_costs"`
	CategoryItemUnitsCosts map[string]CategoryCosts     `json:"category_item_unit_costs"`
	CategoryUseItem        bool                         `json:"category_use_item"`
	KindCategoryUnitsCost  map[string]CategoryItemCosts `json:"kind_category_units_costs"`
}

type ServerlessSkuCost struct {
	TotalCostRawPrice  decimal.Decimal
	SpendLimitRawPrice decimal.Decimal
	FreeCreditRawPrice decimal.Decimal
}

type Price struct {
	Order         int32           `json:"order"`
	CostPrice     decimal.Decimal `json:"cost_price"`
	RawPrice      decimal.Decimal `json:"raw_price"`
	DiscountPrice decimal.Decimal `json:"discount_price"`
}

type LabelCosts map[string]CategoryCosts

type UnitCosts struct {
	CostID        string            `json:"cost_id"`
	CostTimeStamp int64             `json:"cost_time_stamp"`
	Unit          string            `json:"unit"`
	Cost          int               `json:"cost"`
	Labels        map[string]string `json:"labels"`

	CostPrice       decimal.Decimal `json:"price"`
	RawPrice        decimal.Decimal `json:"raw_price"`
	PackageID       uint            `json:"package_id"`
	PackageVersion  uint            `json:"package_version"`
	BindedPackageID uint            `json:"binded_package_id"`
	ConsumedSource  string          `json:"consumed_source"`
	InvoiceID       int             `json:"invoice_id"`
	Discount        decimal.Decimal `json:"discount"`
	Internal        bool            `json:"internal"`
}

type CategoryCosts struct {
	Price
	Order              int32            `json:"order"`
	UnitsCosts         []UnitCosts      `json:"unit_costs,omitempty"` // unused?
	CategoryUnitsCosts map[string]Price `json:"category_unit_costs"`
}

type CategoryItemCosts struct {
	Order int32 `json:"order"`
	Price
	UnitsCosts             []UnitCosts              `json:"unit_costs,omitempty"`
	CategoryItemUnitsCosts map[string]CategoryCosts `json:"category_item_unit_costs"`
}

type CompleteCostExplorerResult struct {
	InternalCosts CostExplorerResult `json:"internal_costs"`
	ExternalCosts CostExplorerResult `json:"external_costs"`
}

// https://pingcap-cn.feishu.cn/wiki/RtDLw6QPriA9Vpke0wGc0sb9nDc
type BillingNotificationReq struct {
	TriggerID    string            `json:"trigger_id"`     // 必填
	Payload      map[string]string `json:"payload"`        // 根据模版内容选填
	TargetIDList []string          `json:"target_id_list"` // 将tenantID或projectID放入
	Roles        []string          `json:"roles"`
	ScopeType    string            `json:"scope_type"`
	WorkFlowID   string            `json:"worker_flow_id"` // 必填
	IsTest       bool              `json:"is_test"`        // 根据真实情况填写
	IsSendNow    bool              `json:"is_send_now"`
	ScheduleTime time.Time         `json:"schedule_time"` // IOS utc 时间
	Operator     string            `json:"operator"`      // API + "api Operator Name"
}

type ActionCheckReq struct {
	Resource Resource `json:"resource"`
	Action   string   `json:"action"`
}
type Resource struct {
	Type string `json:"type"`
}

// {"pass": true, "reason": ""}
type ActionCheckResp struct {
	Pass bool `json:"pass"`
	// Reason is the reason why the action is not allowed.
	Reason string `json:"reason"`
}
type BillingSubCostExplorerResult struct {
	// free credit
	RuFreeCost       decimal.Decimal
	RowStoreFreeCost decimal.Decimal
	ColumnarFreeCost decimal.Decimal
	// spent cost
	RuSpentCost       decimal.Decimal
	RowStoreSpentCost decimal.Decimal
	ColumnarSpentCost decimal.Decimal
}

type BillingFacade interface {
	GetPackage(ctx context.Context, id uint64) (*BillingPackage, bizerr.BizError)
	UpdatePackage(ctx context.Context, id uint64, pkg *BillingPackage4Update) bizerr.BizError
	CreateOrUpdateUnits(ctx context.Context, units []*BillingUnit) bizerr.BizError
	CalcFee(ctx context.Context, reports []*BillingReport) (map[string]*BillingCharge, bizerr.BizError)
	Measure(ctx context.Context, reports []*BillingReport) bizerr.BizError
	GetPaymentMethods(ctx context.Context, id uint64) ([]*StripePaymentMethod, bizerr.BizError)
	GetTenantPlan(ctx context.Context, id uint64) (*TenantPlanInfo, bizerr.BizError)
	ModifySpendLimit(ctx context.Context, setting *SpendLimitSetting) bizerr.BizError
	SetSpendLimit(ctx context.Context, setting *SpendLimitSetting) bizerr.BizError
	GetSpendLimit(ctx context.Context, tenantId uint64, clusterId uint64) (*SpendLimitSetting, bizerr.BizError)
	DeleteSpendLimit(ctx context.Context, tenantId uint64, spendLimitSettingId uint64) bizerr.BizError
	GetSpendLimitSettings(ctx context.Context, tenantId uint64) ([]*SpendLimitSetting, bizerr.BizError)
	GetSpendLimitInfo(ctx context.Context, setting *SpendLimitSetting) (*SpendLimitInfo, bizerr.BizError)
	CalculateCost(
		ctx context.Context,
		tenantId uint64,
		costQuery *CostExplorerInternalQuery,
	) (*CostExplorerResult, bizerr.BizError)
	CostExplorerV2(
		ctx context.Context,
		tenantId uint64,
		costQuery *CostExplorerInternalQuery,
	) (*CompleteCostExplorerResult, bizerr.BizError)
	SendNotification(ctx context.Context, tenantId uint64, req *BillingNotificationReq) bizerr.BizError
	BillingActionCheck(ctx context.Context, tenantId uint64) (*ActionCheckResp, error)
	GetInvoiceLimit(ctx context.Context, tenantId uint64, clusterId uint64) (uint32, bizerr.BizError)
}
