// Copyright 2023 CUE Labs AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ocirequest

import (
	"net/url"
	"testing"

	"github.com/go-quicktest/qt"
)

var parseRequestTests = []struct {
	testName string
	method   string
	url      string

	wantRequest   *Request
	wantError     string
	wantConstruct string
}{{
	testName: "ping",
	method:   "GET",
	url:      "/v2",
	wantRequest: &Request{
		Kind: ReqPing,
	},
	wantConstruct: "/v2/",
}, {
	testName: "ping",
	method:   "GET",
	url:      "/v2/",
	wantRequest: &Request{
		Kind: ReqPing,
	},
}, {
	testName: "getBlob",
	method:   "GET",
	url:      "/v2/foo/bar/blobs/sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
	wantRequest: &Request{
		Kind:   ReqBlobGet,
		Repo:   "foo/bar",
		Digest: "sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
	},
}, {
	testName:  "getBlobInvalidDigest",
	method:    "GET",
	url:       "/v2/foo/bar/blobs/sha256:wrong",
	wantError: `badly formed digest`,
}, {
	testName:  "getBlobInvalidRepo",
	method:    "GET",
	url:       "/v2/foo/bAr/blobs/sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
	wantError: `invalid repository name`,
}, {
	testName: "startUpload",
	method:   "POST",
	url:      "/v2/somerepo/blobs/uploads/",
	wantRequest: &Request{
		Kind: ReqBlobStartUpload,
		Repo: "somerepo",
	},
}, {
	testName: "uploadChunk",
	method:   "PATCH",
	url:      "/v2/somerepo/blobs/uploads/YmxhaGJsYWg",
	wantRequest: &Request{
		Kind:     ReqBlobUploadChunk,
		Repo:     "somerepo",
		UploadID: "blahblah",
	},
}, {
	testName: "uploadChunk",
	method:   "PATCH",
	url:      "/v2/somerepo/blobs/uploads/YmxhaGJsYWg",
	wantRequest: &Request{
		Kind:     ReqBlobUploadChunk,
		Repo:     "somerepo",
		UploadID: "blahblah",
	},
}, {
	testName:  "badlyFormedUploadDigest",
	method:    "POST",
	url:       "/v2/foo/blobs/uploads?digest=sha256:fake",
	wantError: "badly formed digest",
}, {
	testName: "getUploadInfo",
	method:   "GET",
	url:      "/v2/myorg/myrepo/blobs/uploads/YmxhaGJsYWg",
	wantRequest: &Request{
		Kind:     ReqBlobUploadInfo,
		Repo:     "myorg/myrepo",
		UploadID: "blahblah",
	},
}, {
	testName: "mount",
	method:   "POST",
	url:      "/v2/x/y/blobs/uploads/?mount=sha256:c659529df24a1878f6df8d93c652280235a50b95e862d8e5cb566ee5b9ed6386&from=somewhere/other",
	wantRequest: &Request{
		Kind:     ReqBlobMount,
		Repo:     "x/y",
		Digest:   "sha256:c659529df24a1878f6df8d93c652280235a50b95e862d8e5cb566ee5b9ed6386",
		FromRepo: "somewhere/other",
	},
}, {
	testName: "mount2",
	method:   "POST",
	url:      "/v2/myorg/other/blobs/uploads/?from=myorg%2Fmyrepo&mount=sha256%3Ad647b322fff1e9dcb828ee67a6c6d1ed0ceef760988fdf54f9cfdeb96186e001",
	wantRequest: &Request{
		Kind:     ReqBlobMount,
		Repo:     "myorg/other",
		Digest:   "sha256:d647b322fff1e9dcb828ee67a6c6d1ed0ceef760988fdf54f9cfdeb96186e001",
		FromRepo: "myorg/myrepo",
	},
}, {
	testName: "mountWithNoFrom",
	method:   "POST",
	url:      "/v2/x/y/blobs/uploads/?mount=sha256:c659529df24a1878f6df8d93c652280235a50b95e862d8e5cb566ee5b9ed6386",
	wantRequest: &Request{
		Kind: ReqBlobStartUpload,
		Repo: "x/y",
	},
	wantConstruct: "/v2/x/y/blobs/uploads/",
}, {
	testName: "manifestHead",
	method:   "HEAD",
	url:      "/v2/myorg/myrepo/manifests/sha256:681aef2367e055f33cb8a6ab9c3090931f6eefd0c3ef15c6e4a79bdadfdb8982",
	wantRequest: &Request{
		Kind:   ReqManifestHead,
		Repo:   "myorg/myrepo",
		Digest: "sha256:681aef2367e055f33cb8a6ab9c3090931f6eefd0c3ef15c6e4a79bdadfdb8982",
	},
}}

func TestParseRequest(t *testing.T) {
	for _, test := range parseRequestTests {
		t.Run(test.testName, func(t *testing.T) {
			u, err := url.Parse(test.url)
			if err != nil {
				t.Fatal(err)
			}
			rreq, err := Parse(test.method, u)
			if test.wantError != "" {
				qt.Assert(t, qt.ErrorMatches(err, test.wantError))
				// TODO http code
				return
			}
			qt.Assert(t, qt.IsNil(err))
			qt.Assert(t, qt.DeepEquals(rreq, test.wantRequest))
			method, ustr := rreq.Construct()
			if test.wantConstruct == "" {
				test.wantConstruct = test.url
			}

			qt.Check(t, qt.Equals(method, test.method))
			qt.Check(t, qt.Equals(canonURL(ustr), canonURL(test.wantConstruct)))
		})
	}
}

func canonURL(ustr string) string {
	u, err := url.Parse(ustr)
	if err != nil {
		panic(err)
	}
	qv := u.Query()
	if len(qv) == 0 {
		return ustr
	}
	u.RawQuery = qv.Encode()
	return u.String()
}
