package oci

import (
	"fmt"
	"net/url"
	"testing"

	"github.com/opencontainers/go-digest"
	"github.com/stretchr/testify/require"
)

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

	tests := []struct {
		name                string
		registry            string
		path                string
		expectedName        string
		expectedDgst        digest.Digest
		expectedTag         string
		expectedRef         string
		expectedKind        DistributionKind
		execptedIsLatestTag bool
	}{
		{
			name:                "manifest tag",
			registry:            "example.com",
			path:                "/v2/foo/bar/manifests/hello-world",
			expectedName:        "foo/bar",
			expectedDgst:        "",
			expectedTag:         "hello-world",
			expectedRef:         "example.com/foo/bar:hello-world",
			expectedKind:        DistributionKindManifest,
			execptedIsLatestTag: false,
		},
		{
			name:                "manifest with latest tag",
			registry:            "example.com",
			path:                "/v2/test/manifests/latest",
			expectedName:        "test",
			expectedDgst:        "",
			expectedTag:         "latest",
			expectedRef:         "example.com/test:latest",
			expectedKind:        DistributionKindManifest,
			execptedIsLatestTag: true,
		},
		{
			name:                "manifest digest",
			registry:            "docker.io",
			path:                "/v2/library/nginx/manifests/sha256:0a404ca8e119d061cdb2dceee824c914cdc69b31bc7b5956ef5a520436a80d39",
			expectedName:        "library/nginx",
			expectedDgst:        digest.Digest("sha256:0a404ca8e119d061cdb2dceee824c914cdc69b31bc7b5956ef5a520436a80d39"),
			expectedTag:         "",
			expectedRef:         "sha256:0a404ca8e119d061cdb2dceee824c914cdc69b31bc7b5956ef5a520436a80d39",
			expectedKind:        DistributionKindManifest,
			execptedIsLatestTag: false,
		},
		{
			name:                "blob digest",
			registry:            "docker.io",
			path:                "/v2/library/nginx/blobs/sha256:295c7be079025306c4f1d65997fcf7adb411c88f139ad1d34b537164aa060369",
			expectedName:        "library/nginx",
			expectedDgst:        digest.Digest("sha256:295c7be079025306c4f1d65997fcf7adb411c88f139ad1d34b537164aa060369"),
			expectedTag:         "",
			expectedRef:         "sha256:295c7be079025306c4f1d65997fcf7adb411c88f139ad1d34b537164aa060369",
			expectedKind:        DistributionKindBlob,
			execptedIsLatestTag: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			u := &url.URL{
				Path:     tt.path,
				RawQuery: fmt.Sprintf("ns=%s", tt.registry),
			}
			dist, err := ParseDistributionPath(u)
			require.NoError(t, err)
			require.Equal(t, tt.expectedName, dist.Name)
			require.Equal(t, tt.expectedDgst, dist.Digest)
			require.Equal(t, tt.expectedTag, dist.Tag)
			require.Equal(t, tt.expectedRef, dist.Reference())
			require.Equal(t, tt.expectedKind, dist.Kind)
			require.Equal(t, tt.registry, dist.Registry)
			require.Equal(t, tt.path, dist.URL().Path)
			require.Equal(t, tt.registry, dist.URL().Query().Get("ns"))
			require.Equal(t, tt.execptedIsLatestTag, dist.IsLatestTag())
		})
	}
}

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

	tests := []struct {
		name          string
		url           *url.URL
		expectedError string
	}{
		{
			name: "invalid path",
			url: &url.URL{
				Path:     "/v2/clyde/v0.0.1",
				RawQuery: "ns=example.com",
			},
			expectedError: "distribution path could not be parsed",
		},
		{
			name: "blob with tag reference",
			url: &url.URL{
				Path:     "/v2/clyde/blobs/v0.0.1",
				RawQuery: "ns=example.com",
			},
			expectedError: "invalid checksum digest format",
		},
		{
			name: "blob with invalid digest",
			url: &url.URL{
				Path:     "/v2/clyde/blobs/sha256:123",
				RawQuery: "ns=example.com",
			},
			expectedError: "invalid checksum digest length",
		},
		{
			name: "manifest tag with missing registry",
			url: &url.URL{
				Path: "/v2/clyde/manifests/v0.0.1",
			},
			expectedError: "registry parameter needs to be set for tag references",
		},
		{
			name: "manifest with invalid digest",
			url: &url.URL{
				Path: "/v2/clyde/manifests/sha253:foobar",
			},
			expectedError: "unsupported digest algorithm",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			_, err := ParseDistributionPath(tt.url)
			require.EqualError(t, err, tt.expectedError)
		})
	}
}