package gateway

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/tech/sendico/gateway/mntx/internal/service/monetix"
	mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
)

type fixedClock struct {
	now time.Time
}

func (f fixedClock) Now() time.Time {
	return f.now
}

func baseCallback() monetixCallback {
	cb := monetixCallback{
		ProjectID: 42,
	}
	cb.Payment.ID = "payout-1"
	cb.Payment.Status = "success"
	cb.Payment.Sum.Amount = 5000
	cb.Payment.Sum.Currency = "usd"
	cb.Customer.ID = "cust-1"
	cb.Operation.Status = "success"
	cb.Operation.Code = ""
	cb.Operation.Message = "ok"
	cb.Operation.RequestID = "req-1"
	cb.Operation.Provider.PaymentID = "prov-1"
	return cb
}

func TestMapCallbackToState_StatusMapping(t *testing.T) {
	now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
	cfg := monetix.DefaultConfig()

	cases := []struct {
		name            string
		paymentStatus   string
		operationStatus string
		code            string
		expectedStatus  mntxv1.PayoutStatus
		expectedOutcome string
	}{
		{
			name:            "success",
			paymentStatus:   "success",
			operationStatus: "success",
			code:            "0",
			expectedStatus:  mntxv1.PayoutStatus_PAYOUT_STATUS_SUCCESS,
			expectedOutcome: monetix.OutcomeSuccess,
		},
		{
			name:            "processing",
			paymentStatus:   "processing",
			operationStatus: "success",
			code:            "",
			expectedStatus:  mntxv1.PayoutStatus_PAYOUT_STATUS_WAITING,
			expectedOutcome: monetix.OutcomeProcessing,
		},
		{
			name:            "decline",
			paymentStatus:   "failed",
			operationStatus: "failed",
			code:            "1",
			expectedStatus:  mntxv1.PayoutStatus_PAYOUT_STATUS_FAILED,
			expectedOutcome: monetix.OutcomeDecline,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			cb := baseCallback()
			cb.Payment.Status = tc.paymentStatus
			cb.Operation.Status = tc.operationStatus
			cb.Operation.Code = tc.code

			state, outcome := mapCallbackToState(fixedClock{now: now}, cfg, cb)
			if state.Status != tc.expectedStatus {
				t.Fatalf("expected status %v, got %v", tc.expectedStatus, state.Status)
			}
			if outcome != tc.expectedOutcome {
				t.Fatalf("expected outcome %q, got %q", tc.expectedOutcome, outcome)
			}
			if state.Currency != "USD" {
				t.Fatalf("expected currency USD, got %q", state.Currency)
			}
			if !state.UpdatedAt.AsTime().Equal(now) {
				t.Fatalf("expected updated_at %v, got %v", now, state.UpdatedAt.AsTime())
			}
		})
	}
}

func TestFallbackProviderPaymentID(t *testing.T) {
	cb := baseCallback()
	if got := fallbackProviderPaymentID(cb); got != "prov-1" {
		t.Fatalf("expected provider payment id, got %q", got)
	}
	cb.Operation.Provider.PaymentID = ""
	if got := fallbackProviderPaymentID(cb); got != "req-1" {
		t.Fatalf("expected request id fallback, got %q", got)
	}
	cb.Operation.RequestID = ""
	if got := fallbackProviderPaymentID(cb); got != "payout-1" {
		t.Fatalf("expected payment id fallback, got %q", got)
	}
}

func TestVerifyCallbackSignature(t *testing.T) {
	secret := "secret"
	cb := baseCallback()

	sig, err := monetix.SignPayload(cb, secret)
	if err != nil {
		t.Fatalf("failed to sign payload: %v", err)
	}
	cb.Signature = sig
	payload, err := json.Marshal(cb)
	if err != nil {
		t.Fatalf("failed to marshal callback: %v", err)
	}
	if _, err := verifyCallbackSignature(payload, secret); err != nil {
		t.Fatalf("expected valid signature, got %v", err)
	}

	cb.Signature = "invalid"
	payload, err = json.Marshal(cb)
	if err != nil {
		t.Fatalf("failed to marshal callback: %v", err)
	}
	if _, err := verifyCallbackSignature(payload, secret); err == nil {
		t.Fatalf("expected signature mismatch error")
	}
}
