package image

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

	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"
)

var (
	ErrInvalidArgument  = apperror.Define(apperror.InvalidArgument, "image.invalid_argument", "invalid image argument")
	ErrInvalidContent   = apperror.Define(apperror.InvalidArgument, "image.invalid_content", "invalid image content")
	ErrNotFound         = apperror.Define(apperror.NotFound, "image.not_found", "image not found")
	ErrAlreadyExists    = apperror.Define(apperror.AlreadyExists, "image.already_exists", "image already exists")
	ErrConversionFailed = apperror.Define(apperror.Internal, "image.conversion_failed", "image conversion failed")

	ErrRegistryUnauthenticated  = apperror.Define(apperror.Unauthenticated, "registry.unauthenticated", "registry authentication failed")
	ErrRegistryPermissionDenied = apperror.Define(apperror.PermissionDenied, "registry.permission_denied", "registry permission denied")
	ErrRegistryNotFound         = apperror.Define(apperror.NotFound, "registry.not_found", "registry content not found")
	ErrRegistryConflict         = apperror.Define(apperror.Conflict, "registry.conflict", "registry request conflicted")
	ErrRegistryRateLimited      = apperror.Define(apperror.ResourceExhausted, "registry.rate_limited", "registry rate limit exceeded")
	ErrRegistryUnavailable      = apperror.Define(apperror.Unavailable, "registry.unavailable", "registry is unavailable")
	ErrRegistryUpstreamFailure  = apperror.Define(apperror.UpstreamFailure, "registry.upstream_failure", "registry request failed")
)

func translateRegistryError(err error) error {
	if err == nil {
		return nil
	}
	var appErr *apperror.Error
	if errors.As(err, &appErr) {
		return err
	}
	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
		return err
	}
	if errors.Is(err, containerdreference.ErrInvalid) ||
		errors.Is(err, containerdreference.ErrObjectRequired) ||
		errors.Is(err, containerdreference.ErrHostnameRequired) {
		return ErrInvalidArgument.Wrap(err)
	}
	if errdefs.IsNotFound(err) {
		return ErrRegistryNotFound.Wrap(err)
	}
	var unexpected remoteerrors.ErrUnexpectedStatus
	if errors.As(err, &unexpected) {
		switch unexpected.StatusCode {
		case http.StatusUnauthorized:
			return ErrRegistryUnauthenticated.Wrap(err)
		case http.StatusForbidden:
			return ErrRegistryPermissionDenied.Wrap(err)
		case http.StatusNotFound:
			return ErrRegistryNotFound.Wrap(err)
		case http.StatusConflict:
			return ErrRegistryConflict.Wrap(err)
		case http.StatusTooManyRequests:
			return ErrRegistryRateLimited.Wrap(err)
		case http.StatusRequestTimeout, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:
			return ErrRegistryUnavailable.Wrap(err)
		default:
			return ErrRegistryUpstreamFailure.Wrap(err)
		}
	}
	var netErr net.Error
	if errors.As(err, &netErr) {
		return ErrRegistryUnavailable.Wrap(err)
	}
	return err
}