package appidentitykeydeclarationv1

import (
	"bytes"
	"encoding/json"
	"io"
	"testing"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/internal/testvectors"
)

func TestImmutableConformanceVectors(t *testing.T) {
	var document struct {
		SchemaVersion string `json:"schema_version"`
		Vectors       []struct {
			Name                         string `json:"name"`
			ComponentKind                string `json:"component_kind"`
			AppID                        string `json:"app_id"`
			AppReleaseDigest             string `json:"app_release_digest"`
			NamespaceID                  string `json:"namespace_id"`
			LocalKey                     string `json:"local_key"`
			CanonicalBehavior            string `json:"canonical_behavior"`
			BehaviorDigest               string `json:"behavior_digest"`
			DeclarationComponentID       string `json:"declaration_component_id"`
			CanonicalComponentDescriptor string `json:"canonical_component_descriptor"`
		} `json:"vectors"`
	}
	decoder := json.NewDecoder(bytes.NewReader(ConformanceVectorsJSON()))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&document); err != nil {
		t.Fatal(err)
	}
	if err := decoder.Decode(&struct{}{}); err != io.EOF {
		t.Fatalf("trailing vector JSON: %v", err)
	}
	if document.SchemaVersion != "nexuscrm.app-identity-key-declaration.conformance/v1" || len(document.Vectors) != 2 {
		t.Fatalf("vector envelope = %#v", document)
	}
	fixture := newPackageFixture(t,
		fixtureDeclarationSpec{componentKind: ProfileComponentKind, namespaceID: fixtureNamespace, localKey: "billing.operator"},
		fixtureDeclarationSpec{componentKind: RoleComponentKind, namespaceID: fixtureNamespace, localKey: "billing.operator"},
	)
	verified, declarations, err := VerifyPackageDeclarations(fixture.pkg)
	if err != nil {
		t.Fatal(err)
	}
	seen := map[string]bool{}
	for _, vector := range document.Vectors {
		t.Run(vector.Name, func(t *testing.T) {
			if vector.Name == "" || seen[vector.Name] {
				t.Fatalf("blank or duplicate vector name %q", vector.Name)
			}
			seen[vector.Name] = true
			index := findComponentIndex(t, fixture, vector.ComponentKind, vector.LocalKey)
			component := fixture.pkg.Bundle.Components[index]
			descriptor, descriptorErr := appauthority.CanonicalComponentDescriptor(component)
			if descriptorErr != nil {
				t.Fatal(descriptorErr)
			}
			if vector.AppID != verified.Manifest.AppID || vector.AppReleaseDigest != verified.AppReleaseDigest ||
				vector.NamespaceID != component.NamespaceID || vector.LocalKey != component.BusinessKey ||
				vector.CanonicalBehavior != string(fixture.pkg.ComponentBlobs[index].CanonicalBehavior) ||
				vector.BehaviorDigest != component.ContentDigest || vector.DeclarationComponentID != component.ComponentID ||
				vector.CanonicalComponentDescriptor != string(descriptor) {
				t.Fatalf("immutable vector drifted:\nvector=%#v\ncomponent=%#v\nbehavior=%s\ndescriptor=%s", vector, component, fixture.pkg.ComponentBlobs[index].CanonicalBehavior, descriptor)
			}
			switch vector.ComponentKind {
			case ProfileComponentKind:
				if len(declarations.Profiles) != 1 || declarations.Profiles[0].DeclarationComponentID != vector.DeclarationComponentID {
					t.Fatalf("profile declarations = %#v", declarations.Profiles)
				}
			case RoleComponentKind:
				if len(declarations.Roles) != 1 || declarations.Roles[0].DeclarationComponentID != vector.DeclarationComponentID {
					t.Fatalf("role declarations = %#v", declarations.Roles)
				}
			default:
				t.Fatalf("unknown vector component kind %q", vector.ComponentKind)
			}
		})
	}
}

func TestConformanceVectorsJSONReturnsDefensiveCopy(t *testing.T) {
	testvectors.AssertDefensiveCopy(t, ConformanceVectorsJSON)
}
