package store

import (
	"context"
	"testing"
	"time"

	"github.com/tech/sendico/fx/storage/model"
	"github.com/tech/sendico/pkg/db/repository/builder"
	rd "github.com/tech/sendico/pkg/db/repository/decoder"
	"github.com/tech/sendico/pkg/db/storable"
	"github.com/tech/sendico/pkg/merrors"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

func TestRatesStoreUpsertInsert(t *testing.T) {
	ctx := context.Background()
	var inserted *model.RateSnapshot

	repo := &repoStub{
		findOneFn: func(context.Context, builder.Query, storable.Storable) error {
			return merrors.ErrNoData
		},
		insertFn: func(_ context.Context, obj storable.Storable, _ builder.Query) error {
			inserted = cloneRate(t, obj)
			return nil
		},
	}
	store := &ratesStore{logger: zap.NewNop(), repo: repo}

	snapshot := &model.RateSnapshot{RateRef: "r1"}
	if err := store.UpsertSnapshot(ctx, snapshot); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if inserted == nil || inserted.RateRef != "r1" {
		t.Fatalf("expected snapshot to be inserted")
	}
}

func TestRatesStoreUpsertUpdate(t *testing.T) {
	ctx := context.Background()
	existingID := bson.NewObjectID()
	var updated *model.RateSnapshot

	repo := &repoStub{
		findOneFn: func(_ context.Context, _ builder.Query, result storable.Storable) error {
			snap := result.(*model.RateSnapshot)
			snap.SetID(existingID)
			snap.RateRef = "existing"
			return nil
		},
		updateFn: func(_ context.Context, obj storable.Storable) error {
			snap := obj.(*model.RateSnapshot)
			updated = snap
			return nil
		},
	}

	store := &ratesStore{logger: zap.NewNop(), repo: repo}
	toUpdate := &model.RateSnapshot{RateRef: "existing"}
	if err := store.UpsertSnapshot(ctx, toUpdate); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if updated == nil || updated.GetID() == nil || *updated.GetID() != existingID {
		t.Fatalf("expected update to preserve ID")
	}
}

func TestRatesStoreLatestSnapshot(t *testing.T) {
	now := time.Now().UnixMilli()
	repo := &repoStub{
		findManyFn: func(ctx context.Context, _ builder.Query, decode rd.DecodingFunc) error {
			doc := &model.RateSnapshot{RateRef: "latest", AsOfUnixMs: now}
			return runDecoderWithDocs(ctx, t, decode, doc)
		},
	}

	store := &ratesStore{logger: zap.NewNop(), repo: repo}
	res, err := store.LatestSnapshot(context.Background(), model.CurrencyPair{Base: "USD", Quote: "EUR"}, "")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if res.RateRef != "latest" || res.AsOfUnixMs != now {
		t.Fatalf("unexpected snapshot returned: %+v", res)
	}
}
