package resilience

import (
	"context"
	"errors"
	"io"
	"log/slog"
	"testing"
	"time"

	"github.com/aws/smithy-go"

	"wucher/internal/config"
)

func TestExecutor_ExcludedErrorsDoNotTrip(t *testing.T) {
	executor := NewExecutor("ses", config.CircuitBreakerPolicy{
		Enabled:             true,
		Timeout:             time.Second,
		MinRequests:         2,
		FailureRatio:        0.5,
		ConsecutiveFailures: 2,
	}, slog.New(slog.NewJSONHandler(io.Discard, nil)), func(error) Classification {
		return ClassificationExcluded
	})

	for range 2 {
		err := executor.Execute(context.Background(), func(context.Context) error {
			return errors.New("validation error")
		})
		if err == nil {
			t.Fatalf("expected original error")
		}
	}

	counts := executor.Counts()
	if counts.TotalExclusions != 2 {
		t.Fatalf("expected 2 exclusions, got %+v", counts)
	}
	if counts.TotalFailures != 0 {
		t.Fatalf("expected 0 failures, got %+v", counts)
	}
}

func TestExecutor_FailuresTripBreaker(t *testing.T) {
	executor := NewExecutor("sqs", config.CircuitBreakerPolicy{
		Enabled:             true,
		Timeout:             time.Second,
		MinRequests:         1,
		FailureRatio:        0.5,
		ConsecutiveFailures: 2,
	}, slog.New(slog.NewJSONHandler(io.Discard, nil)), nil)

	for range 2 {
		err := executor.Execute(context.Background(), func(context.Context) error {
			return errors.New("dependency down")
		})
		if err == nil {
			t.Fatalf("expected dependency error")
		}
	}

	err := executor.Execute(context.Background(), func(context.Context) error {
		return nil
	})
	var openErr *OpenError
	if !errors.As(err, &openErr) {
		t.Fatalf("expected breaker open error, got %v", err)
	}
}

func TestClassifySQSError(t *testing.T) {
	if got := ClassifySQSError(apiError{code: "AccessDenied", fault: smithy.FaultClient}); got != ClassificationExcluded {
		t.Fatalf("expected client fault excluded, got %v", got)
	}
	if got := ClassifySQSError(apiError{code: "InternalError", fault: smithy.FaultServer}); got != ClassificationFailure {
		t.Fatalf("expected server fault failure, got %v", got)
	}
	if got := ClassifySQSError(apiError{code: "ThrottlingException", fault: smithy.FaultClient}); got != ClassificationFailure {
		t.Fatalf("expected throttling failure, got %v", got)
	}
}

type apiError struct {
	code  string
	fault smithy.ErrorFault
}

func (e apiError) Error() string                 { return e.code }
func (e apiError) ErrorCode() string             { return e.code }
func (e apiError) ErrorMessage() string          { return e.code }
func (e apiError) ErrorFault() smithy.ErrorFault { return e.fault }
