package payments

import (
	"sync"
	"time"

	"github.com/google/uuid"
)

// benefitState is an account's benefit row for one origin: the no-ads term end (nil = none),
// the perpetual forever flag, and the hint count. It is the context-independent stored form;
// the read paths aggregate it over the origins applicable in a context.
type benefitState struct {
	adsPaidUntil *time.Time
	adsForever   bool
	hints        int
}

// walletState is an account's full payments state: chip balances by source and benefits by
// origin. It is the raw, context-independent data every read path filters per execution
// context — the value the cache holds and the store recomputes from the materialized tables.
type walletState struct {
	chips    map[Source]int
	benefits map[Source]benefitState
}

// chipsOf returns the chip balance for a source, zero when the segment has no row (an absent
// segment reads as zero, §2).
func (w walletState) chipsOf(s Source) int { return w.chips[s] }

// benefitOf returns the benefit for an origin, the zero benefit when the origin has no row.
func (w walletState) benefitOf(o Source) benefitState { return w.benefits[o] }

// walletCache is the payments read model: each account's chip/benefit state, keyed by account
// id, read on every ad-eligibility / hint / wallet / gate request and invalidated on every
// payments mutation (spend, grant, fund, refund, merge). It is a write-through cache in front
// of the materialized balances/benefits tables — the same pattern the account-suspension gate
// uses (backend/internal/account/suspension.go) — so the steady-state hot path issues no query
// to the payments schema. It is single-instance, matching the deployment (one shared Store); a
// multi-instance backend would need a shared cache.
type walletCache struct {
	mu sync.RWMutex
	m  map[uuid.UUID]walletState
}

// newWalletCache constructs an empty cache.
func newWalletCache() *walletCache {
	return &walletCache{m: make(map[uuid.UUID]walletState)}
}

// get returns the cached state for an account and whether it was present.
func (c *walletCache) get(id uuid.UUID) (walletState, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	s, ok := c.m[id]
	return s, ok
}

// put stores an account's state.
func (c *walletCache) put(id uuid.UUID, s walletState) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.m[id] = s
}

// invalidate drops an account's entry so the next read reloads it from the materialized tables.
// Called after every mutation once its transaction has committed.
func (c *walletCache) invalidate(id uuid.UUID) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.m, id)
}
