package providers

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"strings"
	"testing"
	"time"
)

func TestRedactJSONValueRemovesSensitiveProviderFields(t *testing.T) {
	t.Parallel()

	redacted := string(RedactJSONValue(map[string]any{
		"legal_name":      "Ada Lovelace",
		"document_number": "P123456789",
		"pan":             "4242424242424242",
		"nested": map[string]any{
			"api_key": "secret-key",
			"note":    "safe",
		},
	}))

	for _, leaked := range []string{"Ada Lovelace", "P123456789", "4242424242424242", "secret-key"} {
		if strings.Contains(redacted, leaked) {
			t.Fatalf("redacted payload leaked %q: %s", leaked, redacted)
		}
	}
	if !strings.Contains(redacted, "safe") {
		t.Fatalf("expected non-sensitive field to remain: %s", redacted)
	}
}

func TestExecutorRetriesAndOpensCircuit(t *testing.T) {
	t.Parallel()

	recorder := &memoryRecorder{}
	executor := NewExecutor(Controls{
		Timeout:                 time.Second,
		MaxAttempts:             2,
		RetryBaseDelay:          time.Millisecond,
		CircuitFailureThreshold: 1,
		CircuitCooldown:         time.Minute,
		AuditEnabled:            true,
	}, recorder, nil)
	providerErr := errors.New("provider down")
	calls := 0

	_, err := Execute(context.Background(), executor, CallMeta{
		ProviderType: "identity",
		ProviderName: "sandbox_identity",
		Operation:    "start_verification",
		BusinessKey:  "user-1",
	}, map[string]string{"user_id": "user-1"}, func(context.Context) (map[string]string, error) {
		calls++
		return nil, providerErr
	})
	if !errors.Is(err, providerErr) {
		t.Fatalf("expected provider error, got %v", err)
	}
	if calls != 2 {
		t.Fatalf("expected two attempts, got %d", calls)
	}
	if len(recorder.records) != 1 || recorder.records[0].Attempts != 2 || recorder.records[0].Status != StatusError {
		t.Fatalf("unexpected audit records: %+v", recorder.records)
	}

	_, err = Execute(context.Background(), executor, CallMeta{
		ProviderType: "identity",
		ProviderName: "sandbox_identity",
		Operation:    "start_verification",
		BusinessKey:  "user-2",
	}, map[string]string{"user_id": "user-2"}, func(context.Context) (map[string]string, error) {
		calls++
		return map[string]string{"status": "should_not_run"}, nil
	})
	if err == nil || !strings.Contains(err.Error(), "circuit is open") {
		t.Fatalf("expected open circuit error, got %v", err)
	}
	if calls != 2 {
		t.Fatalf("open circuit should not call provider again, calls=%d", calls)
	}
}

func TestVerifyWebhookHMACSupportsTimestampPayload(t *testing.T) {
	t.Parallel()

	secret := []byte("webhook-secret-value")
	body := []byte(`{"id":"evt_1"}`)
	timestamp := time.Now().UTC().Format(time.RFC3339)
	mac := hmac.New(sha256.New, secret)
	_, _ = mac.Write([]byte(timestamp + "." + string(body)))
	signature := "sha256=" + hex.EncodeToString(mac.Sum(nil))

	err := VerifyWebhookHMAC(WebhookVerification{
		ProviderType: "identity",
		ProviderName: "sandbox_identity",
		Secret:       secret,
		Signature:    signature,
		Timestamp:    timestamp,
		Body:         body,
		Tolerance:    time.Minute,
	})
	if err != nil {
		t.Fatalf("expected valid signature: %v", err)
	}

	err = VerifyWebhookHMAC(WebhookVerification{
		ProviderType: "identity",
		ProviderName: "sandbox_identity",
		Secret:       secret,
		Signature:    signature,
		Timestamp:    time.Now().UTC().Add(-10 * time.Minute).Format(time.RFC3339),
		Body:         body,
		Tolerance:    time.Minute,
	})
	if err == nil {
		t.Fatal("expected stale timestamp to fail")
	}
}

type memoryRecorder struct {
	records []OutboundCallRecord
}

func (r *memoryRecorder) RecordOutboundCall(_ context.Context, record OutboundCallRecord) error {
	r.records = append(r.records, record)
	return nil
}
