//go:build integration

package integration

import (
	"crypto/ed25519"
	"fmt"

	"github.com/cheqd/cheqd-node/tests/integration/cli"
	"github.com/cheqd/cheqd-node/tests/integration/helpers"
	"github.com/cheqd/cheqd-node/tests/integration/network"
	"github.com/cheqd/cheqd-node/tests/integration/testdata"
	didcli "github.com/cheqd/cheqd-node/x/did/client/cli"
	testsetup "github.com/cheqd/cheqd-node/x/did/tests/setup"
	didtypes "github.com/cheqd/cheqd-node/x/did/types"
	resourcetypes "github.com/cheqd/cheqd-node/x/resource/types"
	"github.com/google/uuid"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("cheqd cli - negative resource", func() {
	var collectionID string
	var did string
	var signInputs []didcli.SignInput
	var resourceID string
	var resourceName string
	var didFeeParams didtypes.FeeParams
	var resourceFeeParams resourcetypes.FeeParams
	var tmpDir string

	BeforeEach(func() {
		// Initialize tmpDir
		tmpDir = GinkgoT().TempDir()

		// Query did fee params
		didRes, err := cli.QueryDidParams()
		Expect(err).To(BeNil())

		didFeeParams = didRes.Params

		// Query resource fee params
		resourceRes, err := cli.QueryResourceParams()
		Expect(err).To(BeNil())

		resourceFeeParams = resourceRes.Params

		// Create a new DID Doc
		collectionID = uuid.NewString()
		did := "did:cheqd:" + network.DidNamespace + ":" + collectionID
		keyId := did + "#key1"

		publicKey, privateKey, err := ed25519.GenerateKey(nil)
		Expect(err).To(BeNil())

		publicKeyMultibase := testsetup.GenerateEd25519VerificationKey2020VerificationMaterial(publicKey)

		payload := didcli.DIDDocument{
			ID: did,
			VerificationMethod: []didcli.VerificationMethod{
				map[string]any{
					"id":                 keyId,
					"type":               "Ed25519VerificationKey2020",
					"controller":         did,
					"publicKeyMultibase": publicKeyMultibase,
				},
			},
			Authentication: []string{keyId},
		}

		signInputs = []didcli.SignInput{
			{
				VerificationMethodID: keyId,
				PrivKey:              privateKey,
			},
		}

		resp, err := cli.CreateDidDoc(tmpDir, payload, signInputs, "", testdata.BASE_ACCOUNT_1, helpers.GenerateFees(didFeeParams.CreateDid.String()))
		Expect(err).To(BeNil())
		Expect(resp.Code).To(BeEquivalentTo(0))

		// Initialize shared values
		resourceID = uuid.NewString()
		resourceName = "TestName"
	})

	It("cannot create resource with missing cli arguments, sign inputs mismatch", func() {
		// Generate a new DID Doc
		collectionId2 := uuid.NewString()

		// Generate extra sign inputs
		keyId2 := did + "#key2"
		_, privateKey2, err := ed25519.GenerateKey(nil)
		Expect(err).To(BeNil())
		signInputs2 := []didcli.SignInput{
			{
				VerificationMethodID: keyId2,
				PrivKey:              privateKey2,
			},
		}

		// Fail to create a resource in non-existing collection
		resourceName = "TestResource"
		resourceVersion := "1.0"
		resourceType := "TestType"
		resourceFile, err := testdata.CreateTestJson(GinkgoT().TempDir())
		Expect(err).To(BeNil())

		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionId2,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot create resource with missing cli arguments"))
		// Fail to create a resource with missing cli arguments
		//   a. missing collection id
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).ToNot(BeNil())

		//  b. missing resource id - works because it is generated by the cli
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(BeNil())

		// c. missing resource name
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		// d. missing resource type
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		// e. missing resource file
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, "", testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		// f. missing sign inputs
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, []didcli.SignInput{}, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		// g. missing account
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, "", helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot create resource with sign inputs mismatch"))
		// Fail to create a resource with sign inputs mismatch
		//   a. sign inputs mismatch
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs2, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		//   b. non-existing key id
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, []didcli.SignInput{
			{
				VerificationMethodID: "non-existing-key-id",
				PrivKey:              signInputs[0].PrivKey,
			},
		}, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		//   c. non-matching private key
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, []didcli.SignInput{
			{
				VerificationMethodID: signInputs[0].VerificationMethodID,
				PrivKey:              privateKey2,
			},
		}, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		//   d. invalid private key
		_, err = cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, []didcli.SignInput{
			{
				VerificationMethodID: signInputs[0].VerificationMethodID,
				PrivKey:              testdata.GenerateByteEntropy(),
			},
		}, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(HaveOccurred())

		// Finally, create the resource
		res, err := cli.CreateResource(tmpDir, resourcetypes.MsgCreateResourcePayload{
			CollectionId: collectionID,
			Id:           resourceID,
			Name:         resourceName,
			Version:      resourceVersion,
			ResourceType: resourceType,
		}, signInputs, resourceFile, testdata.BASE_ACCOUNT_1, helpers.GenerateFees(resourceFeeParams.Json.String()))
		Expect(err).To(BeNil())
		Expect(res.Code).To(BeEquivalentTo(0))
	})

	It("cannot query a resource with missing cli arguments, non-existing collection, non-existing resource", func() {
		collectionId2 := uuid.NewString()
		resourceId2 := uuid.NewString()

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot query a resource with missing cli arguments"))
		// Fail to query a resource with missing cli arguments
		//   a. missing collection id, resource id
		_, err := cli.QueryResource("", "")
		Expect(err).To(HaveOccurred())

		//   b. missing collection id
		_, err = cli.QueryResource("", resourceId2)
		Expect(err).To(HaveOccurred())

		//   c. missing resource id
		_, err = cli.QueryResource(collectionId2, "")
		Expect(err).To(HaveOccurred())

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot query a resource with non-existing collection id"))
		// Fail to query a resource with non-existing collection id
		_, err = cli.QueryResource(collectionId2, resourceID)
		Expect(err).To(HaveOccurred())

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot query a resource with non-existing resource id"))
		// Fail to query a resource with non-existing resource id
		_, err = cli.QueryResource(collectionID, resourceId2)
		Expect(err).To(HaveOccurred())
	})

	It("cannot query resource collection with missing cli arguments, non-existing collection id", func() {
		collectionId2 := uuid.NewString()

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot query resource collection with missing cli arguments"))
		// Fail to query resource collection with missing cli arguments
		//   a. missing collection id
		_, err := cli.QueryResourceCollection("")
		Expect(err).To(HaveOccurred())

		AddReportEntry("Integration", fmt.Sprintf("%sNegative: %s", cli.Purple, "cannot query resource collection with non-existing collection id"))
		// Fail to query resource collection with non-existing collection id
		_, err = cli.QueryResourceCollection(collectionId2)
		Expect(err).To(HaveOccurred())
	})
})
