package store

import (
	"context"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// 积分账本分录类型。
const (
	LedgerGrant  = "grant"  // 充值 / 发放（+）
	LedgerUsage  = "usage"  // 用量消耗（−）
	LedgerAdjust = "adjust" // 人工校正（±）
)

// CreditLedger 是积分账本一条分录（append-only）——扣量 / 充值的事实源。
// 租户当前余额 = SUM(credits_micro)；为快取物化在 sundynix_tenant.credit_balance_micro。
// 表名 sundynix_credit_ledger。
type CreditLedger struct {
	BaseModel
	TenantID     string `gorm:"size:64;index"`
	Kind         string `gorm:"size:16"`       // grant / usage / adjust
	CreditsMicro int64  `gorm:"column:credits_micro"` // 带符号增量（usage 为负）
	Ref          string `gorm:"size:64;index"` // 关联（usage→task_id，grant→订单号）
	Memo         string `gorm:"size:255"`
}

func (CreditLedger) TableName() string { return "sundynix_credit_ledger" }
func (CreditLedger) isTenantScoped()   {}

// UsageRollup 是用量按 租户/天 的聚合快照（配额 / 账单读一行，不扫明细）。
// 消费 UsageEvent 时 upsert 累加。表名 sundynix_usage_rollup。
type UsageRollup struct {
	BaseModel
	TenantID     string `gorm:"size:64;uniqueIndex:idx_rollup_td"`
	Day          string `gorm:"size:8;uniqueIndex:idx_rollup_td"` // 20060102
	TotalTok     int64
	CreditsMicro int64  `gorm:"column:credits_micro"`
	CostMicros   int64  `gorm:"column:cost_micros"`
	TaskCount    int64
	Currency     string `gorm:"size:8"`
}

func (UsageRollup) TableName() string { return "sundynix_usage_rollup" }
func (UsageRollup) isTenantScoped()   {}

// applyUsageCredit 在事务内记 usage 分录（负）+ 扣物化余额（软扣，可为负）。
// 幂等由上层的 usage_event(task_id 唯一) 插入门控保证——仅在新明细插入后调用一次。
func applyUsageCredit(tx *gorm.DB, tenantID, taskID string, creditsMicro int64) error {
	if tenantID == "" || creditsMicro <= 0 {
		return nil
	}
	if err := tx.Create(&CreditLedger{
		TenantID: tenantID, Kind: LedgerUsage, CreditsMicro: -creditsMicro, Ref: taskID, Memo: "task usage",
	}).Error; err != nil {
		return err
	}
	return tx.Model(&Tenant{}).Where("id = ?", tenantID).
		UpdateColumn("credit_balance_micro", gorm.Expr("credit_balance_micro - ?", creditsMicro)).Error
}

// upsertRollup 在事务内累加 租户/天 聚合。
func upsertRollup(tx *gorm.DB, tenantID, day string, tok, creditsMicro, costMicros int64, currency string) error {
	if tenantID == "" {
		return nil
	}
	return tx.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "tenant_id"}, {Name: "day"}},
		DoUpdates: clause.Assignments(map[string]any{
			"total_tok":     gorm.Expr("sundynix_usage_rollup.total_tok + ?", tok),
			"credits_micro": gorm.Expr("sundynix_usage_rollup.credits_micro + ?", creditsMicro),
			"cost_micros":   gorm.Expr("sundynix_usage_rollup.cost_micros + ?", costMicros),
			"task_count":    gorm.Expr("sundynix_usage_rollup.task_count + 1"),
		}),
	}).Create(&UsageRollup{
		TenantID: tenantID, Day: day, TotalTok: tok, CreditsMicro: creditsMicro,
		CostMicros: costMicros, TaskCount: 1, Currency: currency,
	}).Error
}

// UsageDay 是按天聚合的用量（趋势用）。
type UsageDay struct {
	Day          string `json:"day"`
	TotalTok     int64  `json:"total_tok"`
	CreditsMicro int64  `json:"credits_micro"`
	CostMicros   int64  `json:"cost_micros"`
	TaskCount    int64  `json:"task_count"`
}

// UsageTenantSum 是某租户在区间内的用量汇总（+当前余额），供全平台排行。
type UsageTenantSum struct {
	TenantID     string `json:"tenant_id"`
	Name         string `json:"name"`
	TotalTok     int64  `json:"total_tok"`
	CreditsMicro int64  `json:"credits_micro"`
	CostMicros   int64  `json:"cost_micros"`
	TaskCount    int64  `json:"task_count"`
	BalanceMicro int64  `json:"balance_micro"`
}

// UsageTrend 返回按天聚合的用量（tenantID 空=全平台跨租户 SUM）。区间 [from,to] 为 YYYYMMDD。
// 需系统级 ctx（store.WithoutTenant）才跨租户；否则受插件按调用者租户过滤。
func (p *Postgres) UsageTrend(ctx context.Context, tenantID, from, to string) []UsageDay {
	if p.db == nil {
		return nil
	}
	q := p.db.WithContext(ctx).Model(&UsageRollup{}).
		Select("day, sum(total_tok) as total_tok, sum(credits_micro) as credits_micro, sum(cost_micros) as cost_micros, sum(task_count) as task_count").
		Where("day >= ? AND day <= ?", from, to).Group("day").Order("day")
	if tenantID != "" {
		q = q.Where("tenant_id = ?", tenantID)
	}
	var out []UsageDay
	q.Scan(&out)
	return out
}

// UsageByTenant 返回各租户在区间内的用量汇总（含当前余额），按积分消耗降序，供全平台排行。
func (p *Postgres) UsageByTenant(ctx context.Context, from, to string, limit int) []UsageTenantSum {
	if p.db == nil {
		return nil
	}
	if limit <= 0 {
		limit = 20
	}
	var out []UsageTenantSum
	p.db.WithContext(ctx).Table("sundynix_usage_rollup r").
		Select("r.tenant_id, t.name, sum(r.total_tok) as total_tok, sum(r.credits_micro) as credits_micro, "+
			"sum(r.cost_micros) as cost_micros, sum(r.task_count) as task_count, "+
			"coalesce(max(t.credit_balance_micro),0) as balance_micro").
		Joins("left join sundynix_tenant t on t.id = r.tenant_id").
		Where("r.day >= ? AND r.day <= ?", from, to).
		Group("r.tenant_id, t.name").Order("credits_micro desc").Limit(limit).Scan(&out)
	return out
}

// TenantBalance 返回某租户当前物化积分余额（微积分）。
func (p *Postgres) TenantBalance(ctx context.Context, tenantID string) int64 {
	if p.db == nil || tenantID == "" {
		return 0
	}
	var t Tenant
	if err := p.db.WithContext(ctx).Select("credit_balance_micro").First(&t, "id = ?", tenantID).Error; err != nil {
		return 0
	}
	return t.CreditBalanceMicro
}

// GrantCredits 给租户充值 / 发放积分（记 grant 分录 + 增物化余额）。供充值 / 套餐发放 / 人工校正复用。
func (p *Postgres) GrantCredits(ctx context.Context, tenantID, kind string, creditsMicro int64, ref, memo string) error {
	if p.db == nil || tenantID == "" || creditsMicro == 0 {
		return nil
	}
	if kind == "" {
		kind = LedgerGrant
	}
	return p.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(&CreditLedger{
			TenantID: tenantID, Kind: kind, CreditsMicro: creditsMicro, Ref: ref, Memo: memo,
		}).Error; err != nil {
			return err
		}
		return tx.Model(&Tenant{}).Where("id = ?", tenantID).
			UpdateColumn("credit_balance_micro", gorm.Expr("credit_balance_micro + ?", creditsMicro)).Error
	})
}
