package catalogsnapshot

import (
	"bytes"
	"testing"

	"github.com/nexuscrm/kernel/pkg/formula"
	approvalv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalv1"
)

func TestFormulaFunctionBundlesAreImmutableRevisionBoundMutationAuthority(t *testing.T) {
	input := mutationCompileInput()
	input.FormulaFunctionBundles = []FormulaFunctionBundleInput{{CatalogRevision: testRevision, Bundle: testCatalogFormulaBundle(t)}}
	input.Projections = append(input.Projections, RuntimeProjectionInput{
		CatalogRevision:   testRevision,
		AppInstallationID: testInstallA,
		CapabilityID:      "runtime.records.query.v1",
		Profile:           ProjectionRecordQueryV1,
		ObjectAPINames:    []string{testOAuthPolicyObject, "Unrelated", "Workspace", "WorkItem"},
	})

	effective, err := BuildEffectiveMetadata(input)
	if err != nil {
		t.Fatalf("BuildEffectiveMetadata: %v", err)
	}
	openedEffective, err := OpenEffectiveMetadata(effective.Digest(), effective.Bytes())
	if err != nil {
		t.Fatalf("OpenEffectiveMetadata: %v", err)
	}
	artifact, err := CompileEffectiveMetadata(openedEffective)
	if err != nil {
		t.Fatalf("CompileEffectiveMetadata: %v", err)
	}
	snapshot := mustOpenSnapshot(t, artifact)
	if snapshot.doc.Schema != CatalogSnapshotSchemaV15 {
		t.Fatalf("Catalog schema = %q, want %q", snapshot.doc.Schema, CatalogSnapshotSchemaV15)
	}

	mutationArtifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      testMutationCapability,
	})
	if err != nil {
		t.Fatalf("project mutation view: %v", err)
	}
	mutationView := mustOpenView(t, mutationArtifact)
	if mutationView.doc.Schema != RuntimeMetadataViewSchemaV17 {
		t.Fatalf("mutation view schema = %q, want %q", mutationView.doc.Schema, RuntimeMetadataViewSchemaV17)
	}
	bundles := mutationView.FormulaFunctionBundles()
	if len(bundles) != 1 || len(bundles[0].Functions) != 1 || bundles[0].Functions[0].Name != "PACK_PREFIX" {
		t.Fatalf("formula bundles = %#v", bundles)
	}
	bundles[0].Functions[0].Name = "TAMPERED"
	if got := mutationView.FormulaFunctionBundles()[0].Functions[0].Name; got != "PACK_PREFIX" {
		t.Fatalf("formula getter leaked mutable state: %q", got)
	}

	queryArtifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      "runtime.records.query.v1",
	})
	if err != nil {
		t.Fatalf("project query view: %v", err)
	}
	queryView := mustOpenView(t, queryArtifact)
	if len(queryView.FormulaFunctionBundles()) != 0 || bytes.Contains(queryArtifact.Bytes(), []byte("PACK_PREFIX")) {
		t.Fatal("formula mutation authority leaked into record-query view")
	}

	tampered := bytes.Replace(
		mutationArtifact.Bytes(),
		[]byte(RuntimeMetadataViewSchemaV17),
		[]byte(RuntimeMetadataViewSchemaV15),
		1,
	)
	if _, err := OpenRuntimeMetadataView(makeArtifact(ArtifactRuntimeMetadataView, tampered)); err == nil {
		t.Fatal("older Runtime view schema accepted formula authority")
	}
}

func TestApprovalExecutionFormulaVocabularyIsRevisionBoundAndReadDoesNotInheritIt(t *testing.T) {
	input := approvalCompileInput(t)
	input.FormulaFunctionBundles = []FormulaFunctionBundleInput{{
		CatalogRevision: testRevision,
		Bundle:          testISPickvalFormulaBundle(t),
	}}
	snapshot := mustOpenSnapshot(t, mustCompile(t, input))

	executionArtifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      approvalv1.ExecuteCapabilityName,
	})
	if err != nil {
		t.Fatalf("project approval execution view: %v", err)
	}
	execution := mustOpenView(t, executionArtifact)
	if execution.doc.Schema != RuntimeMetadataViewSchemaV19 {
		t.Fatalf("approval execution schema = %q, want %q", execution.doc.Schema, RuntimeMetadataViewSchemaV19)
	}
	tampered := bytes.Replace(
		executionArtifact.Bytes(),
		[]byte(RuntimeMetadataViewSchemaV19),
		[]byte(RuntimeMetadataViewSchemaV16),
		1,
	)
	if _, err := OpenRuntimeMetadataView(makeArtifact(ArtifactRuntimeMetadataView, tampered)); err == nil {
		t.Fatal("v16 approval view accepted v19 formula authority")
	}
	engine, err := execution.NewFormulaEngine()
	if err != nil {
		t.Fatalf("NewFormulaEngine: %v", err)
	}
	result, err := engine.Evaluate(`ISPICKVAL(Status__c, "Submitted")`, &formula.Context{
		Record: map[string]interface{}{"Status__c": "Submitted"},
	})
	if err != nil || result != true {
		t.Fatalf("revision-bound ISPICKVAL result = %#v, %v; want true", result, err)
	}

	readArtifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      approvalv1.ReadCapabilityName,
	})
	if err != nil {
		t.Fatalf("project approval read view: %v", err)
	}
	read := mustOpenView(t, readArtifact)
	if read.doc.Schema != RuntimeMetadataViewSchemaV16 || len(read.FormulaFunctionBundles()) != 0 ||
		bytes.Contains(readArtifact.Bytes(), []byte("ISPICKVAL")) {
		t.Fatalf("approval read inherited formula authority: schema=%q bundles=%d", read.doc.Schema, len(read.FormulaFunctionBundles()))
	}
}

func TestFormulaFunctionBundlesRejectInvalidOrAmbiguousDeclarations(t *testing.T) {
	input := mutationCompileInput()
	bundle := testCatalogFormulaBundle(t)
	input.FormulaFunctionBundles = []FormulaFunctionBundleInput{
		{CatalogRevision: testRevision, Bundle: bundle},
		{CatalogRevision: testRevision, Bundle: bundle},
	}
	if _, err := CompileCatalogSnapshot(input); err == nil {
		t.Fatal("duplicate formula declarations were accepted")
	}
}

func TestFormulaFunctionBundlesRejectMixedSourceRevision(t *testing.T) {
	input := mutationCompileInput()
	input.FormulaFunctionBundles = []FormulaFunctionBundleInput{{
		CatalogRevision: testRevision + 1,
		Bundle:          testCatalogFormulaBundle(t),
	}}
	if _, err := CompileCatalogSnapshot(input); err == nil {
		t.Fatal("formula declarations from another Catalog revision were accepted")
	}
}

func TestPreV15ArtifactsRetainPublishedFormulaReopenCompatibility(t *testing.T) {
	bundle := testCatalogFormulaBundle(t)
	if err := formula.RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime formula bundles: %v", err)
	}
	t.Cleanup(func() { _ = formula.RegisterRuntimeFunctionBundles(nil) })
	if err := formula.RegisterRuntimeFunctionBundles([]formula.FunctionBundle{bundle}); err != nil {
		t.Fatalf("register legacy runtime formula bundle: %v", err)
	}

	input := mutationCompileInput()
	authorship, finalization := connectorInboundFinalizationTestAuthority()
	input.MutationDefinitions = append(input.MutationDefinitions,
		MutationDefinitionInput{CatalogRevision: testRevision, ConnectorInboundAuthorship: &authorship},
		MutationDefinitionInput{CatalogRevision: testRevision, ConnectorInboundFinalization: &finalization},
	)
	artifact, err := CompileCatalogSnapshot(input)
	if err != nil {
		t.Fatalf("compile legacy-shaped snapshot: %v", err)
	}
	var doc catalogDocument
	if err := decodeStrict(artifact.Bytes(), &doc); err != nil {
		t.Fatalf("decode legacy-shaped snapshot: %v", err)
	}
	if doc.Schema == CatalogSnapshotSchemaV15 || doc.Mutation == nil || len(doc.Mutation.ConnectorInboundFinalization) == 0 {
		t.Fatalf("unexpected legacy fixture schema/authority: %q %#v", doc.Schema, doc.Mutation)
	}
	doc.Mutation.ConnectorInboundFinalization[0].OutboundMappings[0].TransformExpression = "PACK_PREFIX(Subject)"
	bytes, err := marshalCanonical(doc)
	if err != nil {
		t.Fatalf("encode legacy artifact: %v", err)
	}
	opened, err := OpenCatalogSnapshot(makeArtifact(ArtifactCatalogSnapshot, bytes))
	if err != nil {
		t.Fatalf("reopen compatible pre-v15 artifact: %v", err)
	}
	projection, err := ProjectRuntimeMetadataView(opened, ProjectionKey{
		AppInstallationID: testInstallA,
		CapabilityID:      testMutationCapability,
	})
	if err != nil {
		t.Fatalf("project compatible pre-v15 artifact: %v", err)
	}
	if _, err := OpenRuntimeMetadataView(projection); err != nil {
		t.Fatalf("reopen compatible pre-v17 Runtime view: %v", err)
	}
}

func TestConnectorMappingValidationUsesOnlyExactRevisionFormulaBundles(t *testing.T) {
	input := mutationCompileInput()
	authorship, finalization := connectorInboundFinalizationTestAuthority()
	finalization.OutboundMappings[0].TransformExpression = "PACK_PREFIX(Subject)"
	input.MutationDefinitions = append(input.MutationDefinitions,
		MutationDefinitionInput{CatalogRevision: testRevision, ConnectorInboundAuthorship: &authorship},
		MutationDefinitionInput{CatalogRevision: testRevision, ConnectorInboundFinalization: &finalization},
	)
	input.FormulaFunctionBundles = []FormulaFunctionBundleInput{{CatalogRevision: testRevision, Bundle: testCatalogFormulaBundle(t)}}
	if _, err := CompileCatalogSnapshot(input); err != nil {
		t.Fatalf("exact revision formula bundle was rejected: %v", err)
	}

	input.FormulaFunctionBundles = nil
	if _, err := CompileCatalogSnapshot(input); err == nil {
		t.Fatal("connector transform inherited formula authority absent from its Catalog revision")
	}
}

func testCatalogFormulaBundle(t *testing.T) formula.FunctionBundle {
	t.Helper()
	bundle, err := formula.ParseFunctionBundlePayload([]byte(`{
		"functions": [{
			"name": "PACK_PREFIX",
			"minArgs": 1,
			"maxArgs": 1,
			"return": {"kind": "fixed", "type": "string"},
			"args": [{"type": "string", "nullable": true}],
			"evaluator": {
				"kind": "formula_expr_v1",
				"params": ["value"],
				"body": "value + \"-probe\""
			}
		}]
	}`))
	if err != nil {
		t.Fatalf("ParseFunctionBundlePayload: %v", err)
	}
	return bundle
}

func testISPickvalFormulaBundle(t *testing.T) formula.FunctionBundle {
	t.Helper()
	bundle, err := formula.ParseFunctionBundlePayload([]byte(`{
		"functions": [{
			"name": "ISPICKVAL",
			"minArgs": 2,
			"maxArgs": 2,
			"return": {"kind": "fixed", "type": "boolean"},
			"args": [
				{"type": "any", "nullable": true},
				{"type": "string", "nullable": true}
			],
			"evaluator": {
				"kind": "formula_expr_v1",
				"params": ["value", "expected"],
				"body": "value == expected"
			}
		}]
	}`))
	if err != nil {
		t.Fatalf("ParseFunctionBundlePayload: %v", err)
	}
	return bundle
}
