package reports

import (
	"context"
	"testing"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/shopspring/decimal"
)

// fakeCache records stored values and can pre-seed a hit.
type fakeCache struct {
	store map[string]string
	gets  int
}

func newFakeCache() *fakeCache { return &fakeCache{store: map[string]string{}} }

func (f *fakeCache) Get(_ context.Context, key string) (string, bool, error) {
	f.gets++
	v, ok := f.store[key]
	return v, ok, nil
}
func (f *fakeCache) Set(_ context.Context, key, value string, _ time.Duration) error {
	f.store[key] = value
	return nil
}

type countingStore struct {
	fakeStore
	kpis int
}

func (c *countingStore) MonthSales(ctx context.Context, from, to time.Time) (decimal.Decimal, error) {
	c.kpis++
	return decimal.NewFromInt(100), nil
}

func TestKPIs_ReadsThroughCache(t *testing.T) {
	st := &countingStore{}
	svc := NewService(st, loc())

	// no cache configured: every call recomputes
	if _, err := svc.KPIs(context.Background()); err != nil {
		t.Fatal(err)
	}
	if _, err := svc.KPIs(context.Background()); err != nil {
		t.Fatal(err)
	}
	if st.kpis != 2 {
		t.Errorf("store hits without cache = %d, want 2", st.kpis)
	}

	// with cache: second call is served from cache, store not hit again
	st.kpis = 0
	c := newFakeCache()
	svc.WithCache(c, time.Minute)
	if _, err := svc.KPIs(context.Background()); err != nil {
		t.Fatal(err)
	}
	if _, err := svc.KPIs(context.Background()); err != nil {
		t.Fatal(err)
	}
	if st.kpis != 1 {
		t.Errorf("store hits with cache = %d, want 1", st.kpis)
	}
	if c.gets != 2 {
		t.Errorf("cache gets = %d, want 2", c.gets)
	}
}

func TestKPIs_CachedPayloadReused(t *testing.T) {
	st := &countingStore{}
	c := newFakeCache()
	svc := NewService(st, loc()).WithCache(c, time.Minute)

	first, err := svc.KPIs(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	second, err := svc.KPIs(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if first.MonthSales.String() != "100" || second.MonthSales.String() != "100" {
		t.Errorf("cached values = %s/%s, want 100/100", first.MonthSales, second.MonthSales)
	}
	if len(c.store) != 1 {
		t.Errorf("cache entries = %d, want 1", len(c.store))
	}
}

func (f *countingStore) Pool() *pgxpool.Pool { return nil }
