package layoutbundleslotv1

import (
	"bytes"
	"encoding/json"
	"io"
	"strconv"
	"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"`
			AppID                        string  `json:"app_id"`
			NamespaceID                  string  `json:"namespace_id"`
			LocalComponentKey            string  `json:"local_component_key"`
			DefinitionVersion            string  `json:"definition_version"`
			TargetComponentID            string  `json:"target_component_id"`
			PreviousTargetComponentID    *string `json:"previous_target_component_id,omitempty"`
			CanonicalBehavior            string  `json:"canonical_behavior"`
			BehaviorDigest               string  `json:"behavior_digest"`
			DeclarationComponentID       string  `json:"declaration_component_id"`
			CanonicalComponentDescriptor string  `json:"canonical_component_descriptor"`
			RevisionDigest               string  `json:"revision_digest"`
		} `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.layout-bundle-slot.conformance/v1" || len(document.Vectors) != 2 {
		t.Fatalf("vector envelope = %#v", document)
	}
	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
			version, err := strconv.ParseUint(vector.DefinitionVersion, 10, 64)
			if err != nil {
				t.Fatal(err)
			}
			targetBusinessKey := "Issue"
			if version == 2 {
				targetBusinessKey = "IssueV2"
			}
			fixture, declaration := fixtureDeclaration(t, version, targetBusinessKey, vector.PreviousTargetComponentID)
			component := fixture.pkg.Bundle.Components[fixture.slotIndex]
			descriptor, err := appauthority.CanonicalComponentDescriptor(component)
			if err != nil {
				t.Fatal(err)
			}
			behavior := fixture.pkg.ComponentBlobs[fixture.slotIndex].CanonicalBehavior
			if vector.AppID != declaration.AppID || vector.NamespaceID != declaration.NamespaceID ||
				vector.LocalComponentKey != string(declaration.LocalComponentKey) || vector.TargetComponentID != declaration.TargetComponentID ||
				vector.CanonicalBehavior != string(behavior) || vector.BehaviorDigest != declaration.BehaviorDigest ||
				vector.DeclarationComponentID != declaration.DeclarationComponentID ||
				vector.CanonicalComponentDescriptor != string(descriptor) || vector.RevisionDigest != declaration.RevisionDigest {
				t.Fatalf("immutable vector drifted:\nvector=%#v\ndeclaration=%#v\nbehavior=%s\ndescriptor=%s", vector, declaration, behavior, descriptor)
			}
		})
	}
}

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