package billing

import (
	"context"
	"time"

	"github.com/shopspring/decimal"
)

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

type ChangeBindedDiscountInput struct {
	BindedDiscount  *decimal.Decimal `json:"binded_discount"`
	ChangerTenantID uint             `json:"changer_tenant_id"`
	ChangerUserID   uint             `json:"changer_user_id"`
}

type CreditsChargeInfoInput struct {
	ChargeCount     decimal.Decimal `json:"charge_count"`
	ChargerTenantID uint            `json:"charger_tenant_id"`
	ChargerUserID   uint            `json:"charger_user_id"`
	ChargeReason    string          `json:"charge_reason"`
}

type CreditsChargeInfoMigrateInput struct {
	ChargeCount     decimal.Decimal `json:"charge_count"`
	ChargerTenantID uint            `json:"charger_tenant_id"`
	ChargerUserID   uint            `json:"charger_user_id"`
	ChargeReason    string          `json:"charge_reason"`
	ChargeTime      time.Time       `json:"charge_time"`
}

type CreditsChargeInfo struct {
	BindedPackageID uint            `json:"binded_package_id"`
	ChargeCount     decimal.Decimal `json:"charge_count"`
	ChargerTenantID uint            `json:"charger_tenant_id"`
	ChargerUserID   uint            `json:"charger_user_id"`
	ChargeReason    string          `json:"charge_reason"`
	ChargeTime      time.Time       `json:"charge_time"`
}

type CreditsChargeHistory struct {
	BindedPackageID uint                `json:"binded_package_id"`
	History         []CreditsChargeInfo `json:"history"`
	TotalAmounts    decimal.Decimal     `json:"total_amounts"`
}

type PaymentMethod struct {
	TenantID        uint   `json:"tenant_id"`
	PaymentMethodID uint   `json:"payment_method_id"`
	PaymentType     string `json:"payment_type"`
	PaymentCode     string `json:"payment_code"`
	Payable         bool   `json:"payable"`
}

type UnitCostsInput struct {
	// optional
	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"`
}

type BillingCharge struct {
	RawFee float64
	Fee    float64
}

// see more in https://pingcap.feishu.cn/wiki/ZMOawVTS0iKUJIk5MCscFlnLnYg
type SKU struct {
	UnitKey     string `json:"unit" validate:"min=1"`
	Description string `json:"description"`
	// optional
	Provider string `json:"provider"`
	// optional
	Category string `json:"category"`
	// optional
	Region string `json:"region"`
	// optional
	EditionCode string `json:"edition_code"`
	// optional
	CommodityCode string `json:"commodity_code"`
	// optional
	PriceType string `json:"price_type"`
	// optional
	Price decimal.Decimal `json:"price"`
	// optional
	PriceExpression map[string]string `json:"price_expression"`
	// optional
	SkuType string `json:"sku_type"`
	// optional
	MeasureUnit string `json:"measure_unit"`
	// optional
	SkuTemplateID uint `json:"sku_template_id"`
	// optional
	Version uint `json:"version"`
}

type ResourceType string

const ResourceTypeDedicatedBackup ResourceType = "dedicated_backup"

type Resource struct {
	Type       ResourceType `json:"type"`
	InstanceID string       `json:"instance_id"`
	Region     string       `json:"region,omitempty"`
	Provider   string       `json:"provider,omitempty"` // UPPER case. AWS, GCP, AZURE
	IsFree     bool         `json:"is_free"`
}

type Action string

const (
	ActionCreate    Action = "add"
	ActionDelete    Action = "remove"
	ActionScaleup   Action = "expand"
	ActionScaledown Action = "shrink"
)

type ActionInfo struct {
	Resource Resource `json:"resource"`
	Action   Action   `json:"action"`
}

type ActionCheckResult struct {
	Pass   bool    `json:"pass"`
	Reason *string `json:"reason"`
}

type BillingHandler interface {
	ChangeUserDiscount(tenantID, bindedPackageID uint64, input *ChangeBindedDiscountInput) error
	ChargeCredits(tenantID, bindedPackageID uint64, input *CreditsChargeInfoInput) error
	GetCredits(tenantID, bindedPackageID uint64) (*decimal.Decimal, error)
	GetCreditsHistory(tenantID, bindedPackageID uint64) (*CreditsChargeHistory, error)
	GetPackageDiscount(packageID uint64) (*decimal.Decimal, error)
	UpdatePackageDiscount(packageID uint64, discount decimal.Decimal) error
	GetBindedPackageDiscount(tenantID, bindedPackageID uint64) (*decimal.Decimal, error)
	UpdateBindedPackageDiscount(tenantID, bindedPackageID uint64, discount *decimal.Decimal) error
	BindPackage(tenantID, packageID uint64) (uint64, error)
	UnBindPackage(tenantID, bindedPackageID uint64) error
	ConfirmStripeSetupIntent(tenantID uint64) error
	CreatePayment(tenantID uint64, paymentType string) error
	ListPayments(tenantID uint64) ([]PaymentMethod, error)
	MeasureCosts(tenantID uint64, cost *UnitCostsInput) error
	CalFee(ctx context.Context, tenantID uint64, record *UnitCostsInput) (*BillingCharge, error)
	MigratePayments() error
	MigrateInvoices() error
	MigrateChargeCredits(bindedPackageID uint64, records []CreditsChargeInfoMigrateInput) error

	// ActionCheck checks whether the action is allowed by billing system.
	// Refer to https://pingcap.feishu.cn/wiki/DxbrwZW4ciNIqHk6G1JcD42Antb
	ActionCheck(ctx context.Context, tenantID uint64, action ActionInfo) (*ActionCheckResult, error)

	// SKU
	ListSKU(ctx context.Context) ([]*SKU, error)
	CreateOrUpdateSKU(ctx context.Context, sku *SKU) (*SKU, error)
	DeleteSKU(ctx context.Context, sku *SKU) error
}

var global BillingHandler = &BillingHandlerImpl{}

func GetHandler() BillingHandler {
	return global
}

func SetHandler(handler BillingHandler) {
	global = handler
}
