package boundary_test

import (
	"path/filepath"
	"regexp"
	"strings"
	"testing"
)

// L0-B / PR-6 anti-regrowth seal.
//
// PR-6 deleted the Salesforce error vocabulary from the app-blind L0 kernel
// package nexuscrm-kernel/pkg/apierr and relocated the live wire shape (SFError,
// the SFCode* constants, SFErrorResponse, SFErrorWithFields, the generic-500
// helpers) to the L1 CRM distribution (backend/distributions/crm/apierr_sf.go).
// The kernel now holds only neutral error types.
//
// The Salesforce error vocabulary is L1-owned (the CRM distribution). App-blind
// L0 must NOT reference it: it neither names apierr.SF* / apierr.Salesforce*
// symbols nor the removed *apierr.SalesforceAPIError type. The two former L0
// consumers (the api-host auth middleware and the global error handler) were
// neutralized via injection seams (AuthFailureProjector / InternalErrorProjector),
// so the count below must stay at ZERO.
//
// boundarygate does not scan backend/, so without this seal the Salesforce error
// vocabulary could silently regrow in the app-blind L0 surface. This is a count
// seal pinned at 0, not a ledger: any reintroduction is a regression, not a
// trackable expansion.
//
// L1 (backend/distributions, crm-tagged) is intentionally NOT scanned — that is
// where the Salesforce error vocabulary now lives.
var l0ApierrSFForbiddenMarkers = []string{
	"apierr.SF",
	"apierr.NewSalesforce",
	"apierr.Salesforce",
	"SalesforceAPIError",
}

// generatedGoHeader matches the canonical "// Code generated ... DO NOT EDIT."
// marker that the Go toolchain recognizes for generated files.
var generatedGoHeader = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)

func TestAppBlindL0DoesNotReferenceSalesforceErrorVocabulary(t *testing.T) {
	root := repoRoot(t)

	// App-blind L0 surface. backend/distributions (L1) is deliberately excluded:
	// that is the new owner of the Salesforce error vocabulary.
	l0Dirs := []string{
		"nexuscrm-kernel/pkg/apierr",
		"nexuscrm-records",
		"nexuscrm-metadata",
		"backend/internal",
		"backend/pkg",
		"backend/cmd",
	}

	for _, relDir := range l0Dirs {
		for _, file := range productionGoFilesRecursive(t, filepath.Join(root, relDir)) {
			rel := toRepoRelative(t, root, file)
			body := readFile(t, file)
			if isGeneratedGoFile(body) {
				continue
			}
			for _, marker := range l0ApierrSFForbiddenMarkers {
				if n := strings.Count(body, marker); n != 0 {
					t.Errorf("%s references Salesforce error vocabulary marker %q %d time(s); "+
						"the Salesforce error vocabulary is L1-owned (backend/distributions/crm), "+
						"app-blind L0 must not reference it. Neutralize the call via an injection "+
						"seam (e.g. AuthFailureProjector / InternalErrorProjector) and keep the "+
						"Salesforce wire shape in the CRM distribution.", rel, marker, n)
				}
			}
		}
	}
}

func isGeneratedGoFile(body string) bool {
	for _, line := range strings.Split(body, "\n") {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" {
			continue
		}
		if generatedGoHeader.MatchString(trimmed) {
			return true
		}
		// The generated marker, if present, appears at the top of the file before
		// the package clause; stop scanning once real declarations begin.
		if strings.HasPrefix(trimmed, "package ") {
			return false
		}
	}
	return false
}
