// Copyright Project Harbor Authors
//
// 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 annotation

import (
	"encoding/base64"
	"encoding/json"
	"io"
	"strings"
	"testing"

	v1 "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/stretchr/testify/suite"

	"github.com/goharbor/harbor/src/pkg/artifact"
	"github.com/goharbor/harbor/src/pkg/distribution"
	"github.com/goharbor/harbor/src/testing/mock"
	reg "github.com/goharbor/harbor/src/testing/pkg/registry"
)

var (
	ormbConfig = `{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Ce Gao <gaoce@caicloud.io>",
    "description": "CNN Model",
    "tags": [
        "cv"
    ],
    "labels": {
        "tensorflow.version": "2.0.0"
    },
    "framework": "TensorFlow",
    "format": "SavedModel",
    "size": 9223372036854775807,
    "metrics": [
        {
            "name": "acc",
            "value": "0.9"
        }
    ],
    "hyperparameters": [
        {
            "name": "batch_size",
            "value": "32"
        }
    ],
    "signature": {
        "inputs": [
            {
                "name": "input_1",
                "size": [
                    224,
                    224,
                    3
                ],
                "dtype": "float64"
            }
        ],
        "outputs": [
            {
                "name": "output_1",
                "size": [
                    1,
                    1000
                ],
                "dtype": "float64"
            }
        ],
        "layers": [
            {
                "name": "conv"
            }
        ]
    },
    "training": {
        "git": {
            "repository": "git@github.com:caicloud/ormb.git",
            "revision": "22f1d8406d464b0c0874075539c1f2e96c253775"
        }
    },
    "dataset": {
        "git": {
            "repository": "git@github.com:caicloud/ormb.git",
            "revision": "22f1d8406d464b0c0874075539c1f2e96c253775"
        }
    }
}`
	ormbManifest = `{
    "schemaVersion":2,
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config":{
        "mediaType":"application/vnd.caicloud.model.config.v1alpha1+json",
        "digest":"sha256:be948daf0e22f264ea70b713ea0db35050ae659c185706aa2fad74834455fe8c",
        "size":187,
        "annotations": {
            "io.goharbor.artifact.v1alpha1.skip-list": "metrics,git"
        }
    },
    "layers":[
        {
            "mediaType": "image/png",
            "digest": "sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0",
            "size": 166015,
            "annotations": { 
                "io.goharbor.artifact.v1alpha1.icon": ""
            }
        },
        {
            "mediaType":"application/tar+gzip",
            "digest":"sha256:eb6063fecbb50a9d98268cb61746a0fd62a27a4af9e850ffa543a1a62d3948b2",
            "size":166022
        }
    ]
}`

	unknownManifestwithIcon = `{
    "schemaVersion":2,
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config": {
        "mediaType": "application/vnd.nhl.peanut.butter.bagel",
        "digest": "sha256:ee29d2e91da0e5dbf6536f5b369148a83ef59b0ce96e49da65dd6c25eb1fa44f",
        "size": 33
    },
    "layers":[
        {
            "mediaType": "image/png",
            "digest": "sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0",
            "size": 166015,
            "annotations": { 
                "io.goharbor.artifact.v1alpha1.icon": ""
            }
        },
        {
            "mediaType":"application/tar+gzip",
            "digest":"sha256:eb6063fecbb50a9d98268cb61746a0fd62a27a4af9e850ffa543a1a62d3948b2",
            "size":166022
        }
    ]
}`
	ormbManifestWithoutSkipList = `{
    "schemaVersion":2,
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config":{
        "mediaType":"application/vnd.caicloud.model.config.v1alpha1+json",
        "digest":"sha256:be948daf0e22f264ea70b713ea0db35050ae659c185706aa2fad74834455fe8c",
        "size":187
    },
    "layers":[
        {
            "mediaType": "image/png",
            "digest": "sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0",
            "size": 166015,
            "annotations": { 
                "io.goharbor.artifact.v1alpha1.icon": ""
            }
        },
        {
            "mediaType":"application/tar+gzip",
            "digest":"sha256:eb6063fecbb50a9d98268cb61746a0fd62a27a4af9e850ffa543a1a62d3948b2",
            "size":166022
        }
    ]
}`
	ormbManifestWithoutIcon = `{
    "schemaVersion":2,
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "config":{
        "mediaType":"application/vnd.caicloud.model.config.v1alpha1+json",
        "digest":"sha256:be948daf0e22f264ea70b713ea0db35050ae659c185706aa2fad74834455fe8c",
        "size":187,
        "annotations": {
            "io.goharbor.artifact.v1alpha1.skip-list": "metrics,git"
        }
    },
    "layers":[
        {
            "mediaType":"application/tar+gzip",
            "digest":"sha256:eb6063fecbb50a9d98268cb61746a0fd62a27a4af9e850ffa543a1a62d3948b2",
            "size":166022
        }
    ]
}`
	ormbIcon = "[encoded data, 18844 characters]"
)

// v1alpha1TestSuite is a test suite of testing v1alpha1 parser
type v1alpha1TestSuite struct {
	suite.Suite
	regCli         *reg.Client
	v1alpha1Parser *v1alpha1Parser
}

func (p *v1alpha1TestSuite) SetupTest() {
	p.regCli = &reg.Client{}
	p.v1alpha1Parser = &v1alpha1Parser{
		regCli: p.regCli,
	}
}
func (p *v1alpha1TestSuite) TestParseUnknownConfig() {
	manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(unknownManifestwithIcon))
	p.Require().Nil(err)
	manifestMediaType, content, err := manifest.Payload()
	p.Require().Nil(err)

	art := &artifact.Artifact{ManifestMediaType: manifestMediaType}

	blob := io.NopCloser(base64.NewDecoder(base64.StdEncoding, strings.NewReader(ormbIcon)))
	p.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(0), blob, nil)
	err = p.v1alpha1Parser.Parse(nil, art, content)
	p.Require().Nil(err)
	p.Len(art.ExtraAttrs, 0)
	p.Equal("sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0", art.Icon)

}
func (p *v1alpha1TestSuite) TestParse() {
	// ormbManifest
	manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(ormbManifest))
	p.Require().Nil(err)
	manifestMediaType, content, err := manifest.Payload()
	p.Require().Nil(err)

	metadata := map[string]interface{}{}
	configBlob := io.NopCloser(strings.NewReader(ormbConfig))
	err = json.NewDecoder(configBlob).Decode(&metadata)
	p.Require().Nil(err)
	art := &artifact.Artifact{ManifestMediaType: manifestMediaType, ExtraAttrs: metadata}

	blob := io.NopCloser(base64.NewDecoder(base64.StdEncoding, strings.NewReader(ormbIcon)))
	p.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(0), blob, nil)
	err = p.v1alpha1Parser.Parse(nil, art, content)
	p.Require().Nil(err)
	p.Len(art.ExtraAttrs, 12)
	p.Equal("CNN Model", art.ExtraAttrs["description"])
	p.Equal("TensorFlow", art.ExtraAttrs["framework"])
	p.Equal([]interface{}{map[string]interface{}{"name": "batch_size", "value": "32"}}, art.ExtraAttrs["hyperparameters"])
	p.Equal("sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0", art.Icon)

	// ormbManifestWithoutSkipList
	// reset the mock
	p.SetupTest()
	manifest, _, err = distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(ormbManifestWithoutSkipList))
	p.Require().Nil(err)
	manifestMediaType, content, err = manifest.Payload()
	p.Require().Nil(err)

	metadata = map[string]interface{}{}
	configBlob = io.NopCloser(strings.NewReader(ormbConfig))
	err = json.NewDecoder(configBlob).Decode(&metadata)
	p.Require().Nil(err)
	art = &artifact.Artifact{ManifestMediaType: manifestMediaType, ExtraAttrs: metadata}

	blob = io.NopCloser(base64.NewDecoder(base64.StdEncoding, strings.NewReader(ormbIcon)))
	p.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(0), blob, nil)
	err = p.v1alpha1Parser.Parse(nil, art, content)
	p.Require().Nil(err)
	p.Len(art.ExtraAttrs, 13)
	p.Equal("CNN Model", art.ExtraAttrs["description"])
	p.Equal("TensorFlow", art.ExtraAttrs["framework"])
	p.Equal([]interface{}{map[string]interface{}{"name": "batch_size", "value": "32"}}, art.ExtraAttrs["hyperparameters"])
	p.Equal("sha256:d923b93eadde0af5c639a972710a4d919066aba5d0dfbf4b9385099f70272da0", art.Icon)

	// ormbManifestWithoutIcon
	// reset the mock
	p.SetupTest()
	manifest, _, err = distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(ormbManifestWithoutIcon))
	p.Require().Nil(err)
	manifestMediaType, content, err = manifest.Payload()
	p.Require().Nil(err)

	metadata = map[string]interface{}{}
	configBlob = io.NopCloser(strings.NewReader(ormbConfig))
	err = json.NewDecoder(configBlob).Decode(&metadata)
	p.Require().Nil(err)
	art = &artifact.Artifact{ManifestMediaType: manifestMediaType, ExtraAttrs: metadata}

	err = p.v1alpha1Parser.Parse(nil, art, content)
	p.Require().Nil(err)
	p.Len(art.ExtraAttrs, 12)
	p.Equal("CNN Model", art.ExtraAttrs["description"])
	p.Equal("TensorFlow", art.ExtraAttrs["framework"])
	p.Equal([]interface{}{map[string]interface{}{"name": "batch_size", "value": "32"}}, art.ExtraAttrs["hyperparameters"])
	p.Equal("", art.Icon)
}

func TestDefaultProcessorTestSuite(t *testing.T) {
	suite.Run(t, &v1alpha1TestSuite{})
}
