package qsnap

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

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

func TestResolve_NotFound(t *testing.T) {
	resolver := New()

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

func TestResolve_Expired(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	_, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return &model.PaymentQuoteRecord{
				QuoteRef:     "quote-ref",
				RequestShape: model.QuoteRequestShapeSingle,
				Items: []*model.PaymentQuoteItemV2{
					{
						Intent: &model.PaymentIntent{Kind: model.PaymentKindPayout},
						Quote:  &model.PaymentQuoteSnapshot{},
						Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
					},
				},
				ExpiresAt: now.Add(-time.Second),
			}, nil
		},
	}, Input{
		OrganizationID: bson.NewObjectID(),
		QuotationRef:   "quote-ref",
	})
	if !errors.Is(err, ErrQuoteExpired) {
		t.Fatalf("expected ErrQuoteExpired, got %v", err)
	}
}

func TestResolve_NotExecutableState(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	_, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return &model.PaymentQuoteRecord{
				QuoteRef:     "quote-ref",
				RequestShape: model.QuoteRequestShapeSingle,
				Items: []*model.PaymentQuoteItemV2{
					{
						Intent: &model.PaymentIntent{Kind: model.PaymentKindPayout},
						Quote:  &model.PaymentQuoteSnapshot{},
						Status: &model.QuoteStatusV2{
							State:       model.QuoteStateBlocked,
							BlockReason: model.QuoteBlockReasonRouteUnavailable,
						},
					},
				},
				ExpiresAt: now.Add(time.Minute),
			}, nil
		},
	}, Input{
		OrganizationID: bson.NewObjectID(),
		QuotationRef:   "quote-ref",
	})
	if !errors.Is(err, ErrQuoteNotExecutable) {
		t.Fatalf("expected ErrQuoteNotExecutable, got %v", err)
	}
}

func TestResolve_NotExecutableIndicative(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	_, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return &model.PaymentQuoteRecord{
				QuoteRef:     "quote-ref",
				RequestShape: model.QuoteRequestShapeSingle,
				Items: []*model.PaymentQuoteItemV2{
					{
						Intent: &model.PaymentIntent{Kind: model.PaymentKindPayout},
						Quote:  &model.PaymentQuoteSnapshot{},
						Status: &model.QuoteStatusV2{State: model.QuoteStateIndicative},
					},
				},
				ExpiresAt: now.Add(time.Minute),
			}, nil
		},
	}, Input{
		OrganizationID: bson.NewObjectID(),
		QuotationRef:   "quote-ref",
	})
	if !errors.Is(err, ErrQuoteNotExecutable) {
		t.Fatalf("expected ErrQuoteNotExecutable, got %v", err)
	}
}

func TestResolve_ShapeMismatch(t *testing.T) {
	now := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)
	resolver := New(Dependencies{Logger: zap.NewNop(), Now: func() time.Time { return now }})

	_, err := resolver.Resolve(context.Background(), &fakeStore{
		getByRefFn: func(context.Context, bson.ObjectID, string) (*model.PaymentQuoteRecord, error) {
			return &model.PaymentQuoteRecord{
				QuoteRef:     "quote-ref",
				RequestShape: model.QuoteRequestShapeSingle,
				Items: []*model.PaymentQuoteItemV2{
					{
						Intent: &model.PaymentIntent{Kind: model.PaymentKindPayout},
						Quote:  &model.PaymentQuoteSnapshot{},
						Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
					},
					{
						Intent: &model.PaymentIntent{Kind: model.PaymentKindPayout},
						Quote:  &model.PaymentQuoteSnapshot{},
						Status: &model.QuoteStatusV2{State: model.QuoteStateExecutable},
					},
				},
				ExpiresAt: now.Add(time.Minute),
			}, nil
		},
	}, Input{
		OrganizationID: bson.NewObjectID(),
		QuotationRef:   "quote-ref",
	})
	if !errors.Is(err, ErrQuoteShapeMismatch) {
		t.Fatalf("expected ErrQuoteShapeMismatch, got %v", err)
	}
}

func TestResolve_InputValidation(t *testing.T) {
	resolver := New()
	orgID := bson.NewObjectID()

	tests := []struct {
		name  string
		store Store
		in    Input
	}{
		{
			name:  "nil store",
			store: nil,
			in: Input{
				OrganizationID: orgID,
				QuotationRef:   "quote-ref",
			},
		},
		{
			name:  "empty org id",
			store: &fakeStore{},
			in: Input{
				QuotationRef: "quote-ref",
			},
		},
		{
			name:  "empty quotation ref",
			store: &fakeStore{},
			in: Input{
				OrganizationID: orgID,
				QuotationRef:   " ",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := resolver.Resolve(context.Background(), tt.store, tt.in)
			if err == nil {
				t.Fatal("expected error")
			}
		})
	}
}
