package qsnap

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

	"github.com/tech/sendico/payments/storage/model"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

func TestResolve_SingleShapeOK(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	orgID := bson.NewObjectID()

	record := &model.PaymentQuoteRecord{
		QuoteRef:     "stored-quote-ref",
		RequestShape: model.QuoteRequestShapeSingle,
		ExpiresAt:    now.Add(time.Minute),
		Items: []*model.PaymentQuoteItemV2{
			{
				Intent: &model.PaymentIntent{
					Ref:  "intent-1",
					Kind: model.PaymentKindPayout,
				},
				Quote: &model.PaymentQuoteSnapshot{
					QuoteRef: "",
				},
				Status: &model.QuoteStatusV2{
					State: model.QuoteStateExecutable,
				},
			},
		},
	}

	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	out, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return record, nil
		},
	}, Input{
		OrganizationID: orgID,
		QuotationRef:   "stored-quote-ref",
	})
	if err != nil {
		t.Fatalf("Resolve returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := out.QuotationRef, "stored-quote-ref"; got != want {
		t.Fatalf("quotation_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := out.IntentSnapshot.Ref, "intent-1"; got != want {
		t.Fatalf("intent.ref mismatch: got=%q want=%q", got, want)
	}
	if out.QuoteSnapshot == nil {
		t.Fatal("expected quote snapshot")
	}
	if got, want := out.QuoteSnapshot.QuoteRef, "stored-quote-ref"; got != want {
		t.Fatalf("quote_snapshot.quote_ref mismatch: got=%q want=%q", got, want)
	}

	out.QuoteSnapshot.QuoteRef = "changed"
	if record.Items[0].Quote.QuoteRef != "" {
		t.Fatalf("expected stored quote snapshot to be unchanged, got %q", record.Items[0].Quote.QuoteRef)
	}
}

func TestResolve_ArrayShapeOK(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	orgID := bson.NewObjectID()

	record := &model.PaymentQuoteRecord{
		QuoteRef:     "batch-like-single",
		RequestShape: model.QuoteRequestShapeBatch,
		Items: []*model.PaymentQuoteItemV2{
			{
				Intent: &model.PaymentIntent{Ref: "intent-1", Kind: model.PaymentKindInternalTransfer},
				Quote:  &model.PaymentQuoteSnapshot{QuoteRef: "snapshot-ref"},
				Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
			},
		},
		ExpiresAt: now.Add(time.Minute),
	}

	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	out, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return record, nil
		},
	}, Input{
		OrganizationID: orgID,
		QuotationRef:   "batch-like-single",
	})
	if err != nil {
		t.Fatalf("Resolve returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := out.IntentSnapshot.Ref, "intent-1"; got != want {
		t.Fatalf("intent.ref mismatch: got=%q want=%q", got, want)
	}
	if out.QuoteSnapshot == nil {
		t.Fatal("expected quote snapshot")
	}
	if got, want := out.QuoteSnapshot.QuoteRef, "snapshot-ref"; got != want {
		t.Fatalf("quote_snapshot.quote_ref mismatch: got=%q want=%q", got, want)
	}
}

func TestResolve_MultiShapeRejectedForSingleExecution(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	orgID := bson.NewObjectID()

	record := &model.PaymentQuoteRecord{
		QuoteRef:     "batch-quote-ref",
		RequestShape: model.QuoteRequestShapeBatch,
		Items: []*model.PaymentQuoteItemV2{
			{
				Intent: &model.PaymentIntent{Ref: "intent-a", Kind: model.PaymentKindPayout},
				Quote:  &model.PaymentQuoteSnapshot{QuoteRef: "batch-quote-ref", DebitAmount: &paymenttypes.Money{Amount: "10", Currency: "USDT"}},
				Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
			},
			{
				Intent: &model.PaymentIntent{Ref: "intent-b", Kind: model.PaymentKindPayout},
				Quote:  &model.PaymentQuoteSnapshot{QuoteRef: "batch-quote-ref", DebitAmount: &paymenttypes.Money{Amount: "15", Currency: "USDT"}},
				Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
			},
		},
		ExpiresAt: now.Add(time.Minute),
	}

	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	out, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return record, nil
		},
	}, Input{
		OrganizationID: orgID,
		QuotationRef:   "batch-quote-ref",
	})
	if out != nil {
		t.Fatalf("expected nil output, got %#v", out)
	}
	if !errors.Is(err, ErrBatchQuoteUnsupported) {
		t.Fatalf("expected ErrBatchQuoteUnsupported, got %v", err)
	}
}
