package reqval

import (
	"strings"
	"testing"

	"go.mongodb.org/mongo-driver/v2/bson"
)

func TestValidate_OK(t *testing.T) {
	v := New()
	orgID := bson.NewObjectID()

	ctx, err := v.Validate(&Req{
		Meta: &Meta{
			OrganizationRef: "  " + orgID.Hex() + "  ",
			Trace: &Trace{
				IdempotencyKey: "  idem-1:alpha  ",
			},
		},
		QuotationRef:     "  quote-ref-1  ",
		ClientPaymentRef: "  client.ref-1  ",
	})
	if err != nil {
		t.Fatalf("Validate returned error: %v", err)
	}
	if ctx == nil {
		t.Fatal("expected ctx")
	}
	if got, want := ctx.OrganizationRef, orgID.Hex(); got != want {
		t.Fatalf("organization_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := ctx.OrganizationID, orgID; got != want {
		t.Fatalf("organization_id mismatch: got=%s want=%s", got.Hex(), want.Hex())
	}
	if got, want := ctx.IdempotencyKey, "idem-1:alpha"; got != want {
		t.Fatalf("idempotency_key mismatch: got=%q want=%q", got, want)
	}
	if got, want := ctx.QuotationRef, "quote-ref-1"; got != want {
		t.Fatalf("quotation_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := ctx.ClientPaymentRef, "client.ref-1"; got != want {
		t.Fatalf("client_payment_ref mismatch: got=%q want=%q", got, want)
	}
}

func TestValidate_ClientPaymentRefOptional(t *testing.T) {
	v := New()
	orgID := bson.NewObjectID()

	ctx, err := v.Validate(&Req{
		Meta: &Meta{
			OrganizationRef: orgID.Hex(),
			Trace: &Trace{
				IdempotencyKey: "idem-1",
			},
		},
		QuotationRef: "quote-1",
	})
	if err != nil {
		t.Fatalf("Validate returned error: %v", err)
	}
	if ctx == nil {
		t.Fatal("expected ctx")
	}
	if ctx.ClientPaymentRef != "" {
		t.Fatalf("expected empty client_payment_ref, got %q", ctx.ClientPaymentRef)
	}
}

func TestValidate_Errors(t *testing.T) {
	orgID := bson.NewObjectID().Hex()
	tooLongIdem := "x" + strings.Repeat("a", maxIdempotencyKeyLen)
	tooLongQuote := "q" + strings.Repeat("a", maxQuotationRefLen)
	tooLongClient := "c" + strings.Repeat("a", maxClientRefLen)

	tests := []struct {
		name string
		req  *Req
	}{
		{
			name: "nil request",
			req:  nil,
		},
		{
			name: "nil meta",
			req:  &Req{},
		},
		{
			name: "empty org ref",
			req: &Req{
				Meta: &Meta{
					Trace: &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "invalid org ref",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: "not-an-object-id",
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "nil trace",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "empty idempotency key",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "   "},
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "too long idempotency key",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: tooLongIdem},
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "bad idempotency key shape",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem key"},
				},
				QuotationRef: "quote-1",
			},
		},
		{
			name: "empty quotation ref",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef: " ",
			},
		},
		{
			name: "too long quotation ref",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef: tooLongQuote,
			},
		},
		{
			name: "bad quotation ref shape",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef: "quote ref",
			},
		},
		{
			name: "too long client payment ref",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef:     "quote-1",
				ClientPaymentRef: tooLongClient,
			},
		},
		{
			name: "bad client payment ref shape",
			req: &Req{
				Meta: &Meta{
					OrganizationRef: orgID,
					Trace:           &Trace{IdempotencyKey: "idem-1"},
				},
				QuotationRef:     "quote-1",
				ClientPaymentRef: "client payment ref",
			},
		},
	}

	v := New()
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx, err := v.Validate(tt.req)
			if err == nil {
				t.Fatalf("expected error, got ctx=%+v", ctx)
			}
		})
	}
}
