package provider

import (
	"context"
	"errors"
	"testing"
	"time"
)

var errBoom = errors.New("boom")

type stubCollector struct {
	name      string
	snapshots []Snapshot
	errors    []error
	calls     int
}

func (s *stubCollector) Name() string { return s.name }

func (s *stubCollector) Refresh(context.Context) (Snapshot, error) {
	idx := s.calls
	s.calls++
	if idx < len(s.errors) && s.errors[idx] != nil {
		return Snapshot{}, s.errors[idx]
	}
	if idx < len(s.snapshots) {
		return s.snapshots[idx], nil
	}
	return Snapshot{RefreshedAt: time.Unix(int64(idx), 0)}, nil
}

func TestRefresherRefreshStoresSuccessfulSnapshot(t *testing.T) {
	want := Snapshot{ReportDate: time.Date(2026, 8, 11, 0, 0, 0, 0, time.UTC), Samples: []BucketSample{{Provider: "aws", Bucket: "b"}}}
	r := NewRefresher(&stubCollector{name: "test", snapshots: []Snapshot{want}}, time.Hour)

	if err := r.Refresh(context.Background()); err != nil {
		t.Fatalf("Refresh() error = %v", err)
	}
	got, ok := r.Snapshot()
	if !ok {
		t.Fatalf("Snapshot() ok = false, want true")
	}
	if got.ReportDate != want.ReportDate || len(got.Samples) != 1 || got.Samples[0].Bucket != "b" {
		t.Fatalf("Snapshot() = %+v, want %+v", got, want)
	}
	if r.Stats().LastSuccess.IsZero() {
		t.Fatalf("LastSuccess is zero")
	}
}

func TestRefresherFailedRefreshKeepsLastSnapshot(t *testing.T) {
	collector := &stubCollector{
		name:      "test",
		snapshots: []Snapshot{{Samples: []BucketSample{{Bucket: "old"}}}},
		errors:    []error{nil, errBoom},
	}
	r := NewRefresher(collector, time.Hour)
	if err := r.Refresh(context.Background()); err != nil {
		t.Fatalf("first Refresh() error = %v", err)
	}
	if err := r.Refresh(context.Background()); err == nil {
		t.Fatalf("second Refresh() error = nil, want error")
	}
	got, ok := r.Snapshot()
	if !ok || got.Samples[0].Bucket != "old" {
		t.Fatalf("Snapshot() = %+v, %v; want old snapshot", got, ok)
	}
	stats := r.Stats()
	if stats.ErrorCount != 1 {
		t.Fatalf("ErrorCount = %d, want 1", stats.ErrorCount)
	}
	if stats.LastError == "" {
		t.Fatalf("LastError is empty")
	}
}

func TestRefresherRunStopsOnContextCancel(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	collector := &stubCollector{name: "test"}
	r := NewRefresher(collector, time.Millisecond)
	done := make(chan struct{})
	go func() {
		r.Run(ctx)
		close(done)
	}()
	cancel()
	select {
	case <-done:
	case <-time.After(time.Second):
		t.Fatalf("Run did not stop after context cancellation")
	}
}
