package boundary_test

import (
	"bytes"
	"encoding/json"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"testing"
)

const runtimeMetadataSnapshotManifest = "docs/refactor/serviceification/runtime-metadata-snapshot-p7b.json"

type runtimeMetadataSnapshotEvidence struct {
	Schema            string                            `json:"schema"`
	EvidenceRevision  string                            `json:"evidence_revision"`
	P7AInventory      string                            `json:"p7a_inventory"`
	A1BContract       runtimeMetadataSnapshotSource     `json:"a1b_contract"`
	Scope             map[string]any                    `json:"scope"`
	ConsumerMap       []runtimeMetadataSnapshotConsumer `json:"consumer_map"`
	P7CAtomicSwitches []string                          `json:"p7c_atomic_switches"`
	ArtifactModel     map[string]string                 `json:"artifact_model"`
	Axes              []map[string]string               `json:"axes"`
	RuntimeProjection map[string]any                    `json:"runtime_projection"`
	BehaviorOracles   []runtimeMetadataSnapshotOracle   `json:"selected_root_behavior_oracles"`
	KnownLegacyDeltas []map[string]string               `json:"known_legacy_deltas"`
	NonServingGuard   map[string]bool                   `json:"non_serving_guard"`
	P7CFollowUp       string                            `json:"p7c_follow_up"`
}

type runtimeMetadataSnapshotSource struct {
	Path          string   `json:"path"`
	Anchor        string   `json:"anchor"`
	ConsumedFacts []string `json:"consumed_facts"`
}

type runtimeMetadataSnapshotOracle struct {
	Path   string `json:"path"`
	Anchor string `json:"anchor"`
	Proves string `json:"proves"`
}

type runtimeMetadataSnapshotConsumer struct {
	ID                 string `json:"id"`
	Role               string `json:"role"`
	SourceFile         string `json:"source_file"`
	SourceAnchor       string `json:"source_anchor"`
	CurrentSource      string `json:"current_source"`
	Fallback           string `json:"fallback"`
	RevisionAssumption string `json:"revision_assumption"`
	P7CSwitch          bool   `json:"p7c_switch"`
}

func TestRuntimeMetadataSnapshotEvidenceIsCompleteAndAnchored(t *testing.T) {
	root := repoRoot(t)
	data := readFile(t, filepath.Join(root, filepath.FromSlash(runtimeMetadataSnapshotManifest)))
	var manifest runtimeMetadataSnapshotEvidence
	decoder := json.NewDecoder(strings.NewReader(data))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&manifest); err != nil {
		t.Fatalf("decode P7b evidence: %v", err)
	}
	if err := decoder.Decode(&struct{}{}); err != io.EOF {
		t.Fatalf("P7b evidence has trailing content: %v", err)
	}
	if manifest.Schema != "nexuscrm.runtime-metadata-snapshot.p7b/v1" {
		t.Fatalf("manifest schema = %q", manifest.Schema)
	}
	if !validCommitSHA(manifest.EvidenceRevision) {
		t.Fatalf("evidence revision = %q", manifest.EvidenceRevision)
	}
	if manifest.P7AInventory != runtimeMetadataClosureManifest {
		t.Fatalf("P7a inventory = %q", manifest.P7AInventory)
	}
	if manifest.A1BContract.Path != "docs/design/app-authority-contract.json" ||
		len(manifest.A1BContract.ConsumedFacts) != 4 {
		t.Fatalf("A1b input contract is incomplete: %#v", manifest.A1BContract)
	}
	a1b := readFile(t, filepath.Join(root, filepath.FromSlash(manifest.A1BContract.Path)))
	if !strings.Contains(a1b, manifest.A1BContract.Anchor) {
		t.Fatalf("A1b handoff lost anchor %q", manifest.A1BContract.Anchor)
	}

	wantConsumers := map[string]bool{
		"runtime-api-command": false, "runtime-api-candidate-assembly": true,
		"runtime-receipts": true, "records-root-schema": true,
		"permissions-read-metadata": true, "analytics-projected-query": true,
		"persistence-read-shape": true, "runtime-worker-command": false,
	}
	if len(manifest.ConsumerMap) != len(wantConsumers) {
		t.Fatalf("consumer count = %d, want %d", len(manifest.ConsumerMap), len(wantConsumers))
	}
	seen := make(map[string]struct{}, len(manifest.ConsumerMap))
	for _, consumer := range manifest.ConsumerMap {
		wantSwitch, expected := wantConsumers[consumer.ID]
		if !expected {
			t.Fatalf("unexpected consumer %q", consumer.ID)
		}
		if _, duplicate := seen[consumer.ID]; duplicate {
			t.Fatalf("duplicate consumer %q", consumer.ID)
		}
		seen[consumer.ID] = struct{}{}
		if consumer.P7CSwitch != wantSwitch {
			t.Fatalf("consumer %q p7c switch = %v, want %v", consumer.ID, consumer.P7CSwitch, wantSwitch)
		}
		if consumer.Role == "" || consumer.CurrentSource == "" || consumer.Fallback == "" || consumer.RevisionAssumption == "" {
			t.Fatalf("consumer %q is incomplete: %#v", consumer.ID, consumer)
		}
		source := readFile(t, filepath.Join(root, filepath.FromSlash(consumer.SourceFile)))
		// P7b deliberately recorded the pre-cutover sources. D1 replaces the API
		// command, candidate assembly, and independent receipt source; D2B replaces
		// the no-work worker command. Their current owners are proved by dedicated
		// serving/worker closure tests rather than keeping historical source paths
		// alive or rewriting historical evidence.
		cutoverSource := consumer.ID == "runtime-api-command" || consumer.ID == "runtime-api-candidate-assembly" ||
			consumer.ID == "runtime-receipts" || consumer.ID == "runtime-worker-command"
		if !cutoverSource && !strings.Contains(source, consumer.SourceAnchor) {
			t.Fatalf("consumer %q lost anchor %q in %s", consumer.ID, consumer.SourceAnchor, consumer.SourceFile)
		}
	}
	if len(manifest.P7CAtomicSwitches) != 5 {
		t.Fatalf("P7c atomic switches = %v", manifest.P7CAtomicSwitches)
	}
	wantAxes := []string{"CatalogRevision", "DefinitionVersion", "EngineGeneration", "SchemaGeneration"}
	var axes []string
	for _, axis := range manifest.Axes {
		if axis["name"] == "" || axis["meaning"] == "" || axis["location"] == "" {
			t.Fatalf("incomplete axis: %#v", axis)
		}
		axes = append(axes, axis["name"])
	}
	sort.Strings(axes)
	if strings.Join(axes, "\x00") != strings.Join(wantAxes, "\x00") {
		t.Fatalf("axes = %v, want %v", axes, wantAxes)
	}
	if manifest.ArtifactModel["package"] != "github.com/nexuscrm/metadata/catalogsnapshot" ||
		manifest.ArtifactModel["compatibility"] == "" || manifest.ArtifactModel["freshness_authority"] == "" {
		t.Fatalf("artifact model is incomplete: %#v", manifest.ArtifactModel)
	}
	wantGuards := []string{
		"package_has_no_serving_selector_or_environment_activation",
		"production_manifests_have_no_snapshot_selector",
		"runtime_api_command_remains_non_serving",
		"runtime_commands_exclude_package_and_future_subpackages",
		"tests_may_instantiate_directly",
	}
	if len(manifest.NonServingGuard) != len(wantGuards) {
		t.Fatalf("non-serving guards = %#v", manifest.NonServingGuard)
	}
	for _, guard := range wantGuards {
		if !manifest.NonServingGuard[guard] {
			t.Fatalf("non-serving guard %q is absent or false", guard)
		}
	}
	if len(manifest.KnownLegacyDeltas) != 4 || strings.TrimSpace(manifest.P7CFollowUp) == "" {
		t.Fatal("legacy deltas and exact P7c handoff must remain explicit")
	}
	if len(manifest.BehaviorOracles) != 2 {
		t.Fatalf("selected-root behavior oracles = %#v", manifest.BehaviorOracles)
	}
	for _, oracle := range manifest.BehaviorOracles {
		if oracle.Path == "" || oracle.Anchor == "" || oracle.Proves == "" {
			t.Fatalf("incomplete selected-root behavior oracle: %#v", oracle)
		}
		source := readFile(t, filepath.Join(root, filepath.FromSlash(oracle.Path)))
		if !strings.Contains(source, oracle.Anchor) {
			t.Fatalf("selected-root oracle lost anchor %q in %s", oracle.Anchor, oracle.Path)
		}
	}
}

func TestCatalogSnapshotServingClosureIsOnlyRuntimeAPI(t *testing.T) {
	root := repoRoot(t)
	backend := filepath.Join(root, "backend")
	apiDeps := runGoList(t, backend, "-deps", "-f", "{{.ImportPath}}", "./cmd/runtime-api/...")
	if !strings.Contains(apiDeps, "github.com/nexuscrm/metadata/catalogsnapshot") {
		t.Fatal("D1 runtime-api closure does not consume the immutable Catalog snapshot")
	}
	for _, command := range []string{"./cmd/runtime-worker/..."} {
		deps := runGoList(t, backend, "-deps", "-f", "{{.ImportPath}}", command)
		for _, dependency := range strings.Fields(deps) {
			if dependency == "github.com/nexuscrm/backend/internal/runtimeproduction" {
				t.Fatalf("non-Runtime command %s contains D1 serving composition owner %q", command, dependency)
			}
		}
	}

	metadataModule := filepath.Join(root, "nexuscrm-metadata")
	packageDeps := runGoList(t, metadataModule, "-deps", "-f", "{{.ImportPath}}", "./catalogsnapshot/...")
	for _, forbidden := range []string{
		"github.com/nexuscrm/backend", "github.com/nexuscrm/persistence",
		"github.com/nexuscrm/records", "github.com/nexuscrm/permissions",
		"github.com/nexuscrm/analytics", "github.com/nexuscrm/kernel/pkg/ports",
	} {
		for _, dependency := range strings.Fields(packageDeps) {
			if dependency == forbidden || strings.HasPrefix(dependency, forbidden+"/") {
				t.Fatalf("Catalog snapshot mechanism depends on serving/repository owner %q", dependency)
			}
		}
	}
}

func TestCatalogSnapshotPackageHasNoServingSelectorOrActivationChannel(t *testing.T) {
	root := repoRoot(t)
	packageRoot := filepath.Join(root, "nexuscrm-metadata", "catalogsnapshot")
	forbiddenFunctions := map[string]struct{}{
		"Activate": {}, "ActivateRevision": {}, "Current": {}, "CurrentDigest": {},
		"Latest": {}, "List": {}, "SelectServing": {}, "SetCurrent": {}, "Watch": {},
	}
	forbiddenSelectors := map[string]struct{}{
		"os.Getenv": {}, "os.LookupEnv": {}, "time.After": {}, "time.NewTicker": {},
		"time.NewTimer": {}, "time.Now": {}, "time.Tick": {}, "time.Until": {},
	}
	if err := filepath.WalkDir(packageRoot, func(path string, entry fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") {
			return nil
		}
		fset := token.NewFileSet()
		file, err := parser.ParseFile(fset, path, nil, 0)
		if err != nil {
			return err
		}
		for _, declaration := range file.Decls {
			function, ok := declaration.(*ast.FuncDecl)
			if ok {
				if _, forbidden := forbiddenFunctions[function.Name.Name]; forbidden {
					t.Fatalf("%s declares serving selector %s", path, function.Name.Name)
				}
			}
		}
		ast.Inspect(file, func(node ast.Node) bool {
			field, ok := node.(*ast.Field)
			if ok {
				for _, name := range field.Names {
					if _, forbidden := forbiddenFunctions[name.Name]; forbidden {
						t.Fatalf("%s declares activation/freshness member %s", path, name.Name)
					}
				}
			}
			selector, ok := node.(*ast.SelectorExpr)
			if !ok {
				return true
			}
			identifier, ok := selector.X.(*ast.Ident)
			if !ok {
				return true
			}
			name := identifier.Name + "." + selector.Sel.Name
			if _, forbidden := forbiddenSelectors[name]; forbidden {
				t.Fatalf("%s contains activation/freshness selector %s", path, name)
			}
			return true
		})
		return nil
	}); err != nil {
		t.Fatalf("scan Catalog snapshot package: %v", err)
	}

	for _, relative := range []string{
		"backend/cmd/runtime-worker", "infra/runtime",
	} {
		path := filepath.Join(root, filepath.FromSlash(relative))
		if err := filepath.WalkDir(path, func(filePath string, entry fs.DirEntry, err error) error {
			if err != nil {
				return err
			}
			if entry.IsDir() || strings.HasSuffix(entry.Name(), "_test.go") {
				return nil
			}
			data, err := os.ReadFile(filePath)
			if err != nil {
				return err
			}
			for _, marker := range [][]byte{
				[]byte("github.com/nexuscrm/metadata/catalogsnapshot"),
				[]byte("nexuscrm.catalog-snapshot/v1"),
				[]byte("nexuscrm.runtime-metadata-view/v1"),
			} {
				if bytes.Contains(data, marker) {
					t.Fatalf("production Runtime assembly artifact %s selects or names P7b marker %q", filePath, marker)
				}
			}
			return nil
		}); err != nil {
			t.Fatalf("scan production Runtime path %s: %v", relative, err)
		}
	}
}

func runGoList(t *testing.T, directory string, args ...string) string {
	t.Helper()
	command := exec.Command("go", append([]string{"list"}, args...)...)
	command.Dir = directory
	output, err := command.CombinedOutput()
	if err != nil {
		t.Fatalf("go list %v in %s: %v\n%s", args, directory, err, output)
	}
	return string(output)
}
