package image

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"testing"

	"github.com/containerd/containerd/v2/core/remotes/docker"
	remoteerrors "github.com/containerd/containerd/v2/core/remotes/errors"
	containerdreference "github.com/containerd/containerd/v2/pkg/reference"
	"github.com/containerd/errdefs"
	"github.com/openeuler/Conch/internal/apperror"
)

func TestTranslateRegistryStatus(t *testing.T) {
	tests := []struct {
		status int
		want   *apperror.Error
	}{
		{http.StatusUnauthorized, ErrRegistryUnauthenticated},
		{http.StatusForbidden, ErrRegistryPermissionDenied},
		{http.StatusNotFound, ErrRegistryNotFound},
		{http.StatusConflict, ErrRegistryConflict},
		{http.StatusTooManyRequests, ErrRegistryRateLimited},
		{http.StatusServiceUnavailable, ErrRegistryUnavailable},
		{http.StatusInternalServerError, ErrRegistryUpstreamFailure},
	}
	for _, tt := range tests {
		t.Run(http.StatusText(tt.status), func(t *testing.T) {
			cause := remoteerrors.ErrUnexpectedStatus{
				StatusCode: tt.status,
				RequestURL: "https://user:secret@registry.example.invalid/private",
			}
			err := translateRegistryError(fmt.Errorf("registry operation: %w", cause))
			if !errors.Is(err, tt.want) {
				t.Fatalf("error = %v, want %s", err, tt.want.Code())
			}
			var retained remoteerrors.ErrUnexpectedStatus
			if !errors.As(err, &retained) {
				t.Fatal("registry cause was not retained")
			}
		})
	}
}

func TestTranslateRegistryErrorPreservesExistingClassificationAndDeadline(t *testing.T) {
	classified := ErrInvalidArgument.Wrap(errors.New("bad reference"))
	if got := translateRegistryError(classified); got != classified {
		t.Fatalf("existing classification was replaced: %v", got)
	}
	if got := translateRegistryError(context.DeadlineExceeded); !errors.Is(got, context.DeadlineExceeded) {
		t.Fatalf("deadline was replaced: %v", got)
	}
}

func TestTranslateRegistryErrorClassifiesResolverNotFound(t *testing.T) {
	cause := fmt.Errorf("resolve manifest: %w", errdefs.ErrNotFound)
	got := translateRegistryError(cause)
	if !errors.Is(got, ErrRegistryNotFound) {
		t.Fatalf("error = %v, want %s", got, ErrRegistryNotFound.Code())
	}
	if !errors.Is(got, errdefs.ErrNotFound) {
		t.Fatal("resolver cause was not retained")
	}
}

func TestTranslateRegistryErrorClassifiesInvalidReference(t *testing.T) {
	resolver := docker.NewResolver(docker.ResolverOptions{})
	for _, tc := range []struct {
		reference string
		cause     error
	}{
		{reference: "12345", cause: containerdreference.ErrObjectRequired},
		{reference: "registry.example/team/image", cause: containerdreference.ErrObjectRequired},
		{reference: "https://registry.example/team/image:latest", cause: containerdreference.ErrInvalid},
		{reference: "/team/image:latest", cause: containerdreference.ErrHostnameRequired},
	} {
		_, _, cause := resolver.Resolve(context.Background(), tc.reference)
		if !errors.Is(cause, tc.cause) {
			t.Fatalf("Resolve(%q) error = %v, want %v", tc.reference, cause, tc.cause)
		}
		got := translateRegistryError(cause)
		if !errors.Is(got, ErrInvalidArgument) {
			t.Fatalf("error = %v, want %s", got, ErrInvalidArgument.Code())
		}
		if !errors.Is(got, tc.cause) {
			t.Fatalf("error = %v, want cause %v", got, tc.cause)
		}
	}
}

func TestTranslateRegistryErrorPreservesUnknownFailure(t *testing.T) {
	cause := errors.New("content store I/O failed")
	err := fmt.Errorf("classify fetched image: %w", cause)
	got := translateRegistryError(err)
	if got != err {
		t.Fatalf("unknown failure was replaced: %v", got)
	}
	var appErr *apperror.Error
	if errors.As(got, &appErr) {
		t.Fatalf("unknown failure was classified as %s", appErr.Code())
	}
}