package paymentapiimp

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/tech/sendico/pkg/auth"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
	"github.com/tech/sendico/server/interface/api/sresponse"
	mutil "github.com/tech/sendico/server/internal/mutil/param"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

func TestGetPayment_FetchesByPaymentRefWithoutOrganizationPath(t *testing.T) {
	orgRef := bson.NewObjectID()
	paymentRef := "payment-123"
	exec := &fakeExecutionClientForGet{
		getPaymentResp: &orchestrationv2.GetPaymentResponse{
			OrganizationRef: orgRef.Hex(),
			Payment: &orchestrationv2.Payment{
				PaymentRef: paymentRef,
			},
		},
	}
	enf := &capturingPaymentEnforcer{allowed: true}
	api := &PaymentAPI{
		logger:        zap.NewNop(),
		execution:     exec,
		enf:           enf,
		oph:           mutil.CreatePH(mservice.Organizations),
		pph:           mutil.CreatePH(mservice.Payments),
		permissionRef: bson.NewObjectID(),
	}

	req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/object/"+paymentRef, nil)
	routeCtx := chi.NewRouteContext()
	routeCtx.URLParams.Add("payments_ref", paymentRef)
	req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx))
	account := &model.Account{}
	account.ID = bson.NewObjectID()

	rr := httptest.NewRecorder()
	handler := api.getPayment(req, account, &sresponse.TokenData{
		Token:      "token",
		Expiration: time.Now().UTC().Add(time.Hour),
	})
	handler.ServeHTTP(rr, req)

	if got, want := rr.Code, http.StatusOK; got != want {
		t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
	}
	if exec.getPaymentReq == nil {
		t.Fatal("expected GetPayment request")
	}
	if got, want := exec.getPaymentReq.GetPaymentRef(), paymentRef; got != want {
		t.Fatalf("payment_ref mismatch: got=%q want=%q", got, want)
	}
	if exec.getPaymentReq.GetMeta() != nil {
		t.Fatalf("expected no organization metadata in request, got=%+v", exec.getPaymentReq.GetMeta())
	}
	if got, want := enf.organizationRef, orgRef; got != want {
		t.Fatalf("permission organization_ref mismatch: got=%s want=%s", got.Hex(), want.Hex())
	}
}

type fakeExecutionClientForGet struct {
	getPaymentReq  *orchestrationv2.GetPaymentRequest
	getPaymentResp *orchestrationv2.GetPaymentResponse
}

func (*fakeExecutionClientForGet) ExecutePayment(context.Context, *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
	return &orchestrationv2.ExecutePaymentResponse{}, nil
}

func (*fakeExecutionClientForGet) ExecuteBatchPayment(context.Context, *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error) {
	return &orchestrationv2.ExecuteBatchPaymentResponse{}, nil
}

func (f *fakeExecutionClientForGet) GetPayment(_ context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
	f.getPaymentReq = req
	return f.getPaymentResp, nil
}

func (*fakeExecutionClientForGet) ListPayments(context.Context, *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
	return &orchestrationv2.ListPaymentsResponse{}, nil
}

func (*fakeExecutionClientForGet) Close() error { return nil }

type capturingPaymentEnforcer struct {
	allowed         bool
	organizationRef bson.ObjectID
}

func (f *capturingPaymentEnforcer) Enforce(_ context.Context, _ bson.ObjectID, _ bson.ObjectID, organizationRef bson.ObjectID, _ bson.ObjectID, _ model.Action) (bool, error) {
	f.organizationRef = organizationRef
	return f.allowed, nil
}

func (*capturingPaymentEnforcer) EnforceBatch(context.Context, []model.PermissionBoundStorable, bson.ObjectID, model.Action) (map[bson.ObjectID]bool, error) {
	//nolint:nilnil // Test stub does not provide batch permissions map.
	return nil, nil
}

func (*capturingPaymentEnforcer) GetRoles(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, error) {
	return nil, nil
}

func (*capturingPaymentEnforcer) GetPermissions(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, []model.Permission, error) {
	return nil, nil, nil
}

var _ auth.Enforcer = (*capturingPaymentEnforcer)(nil)
