package boundary_test

import (
	"bytes"
	"go/parser"
	"go/token"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"testing"
)

const salesforceDerivedBoundaryLedger = "docs/refactor/l0-l1-boundary/salesforce-derived-l0-boundary-ledger.md"

var productDialectImportPrefixes = []string{
	"github.com/nexuscrm/dialect-sf-crm",
	"github.com/nexuscrm/dialect-sf-crm-soql",
}

var salesforceFormulaMarkers = []string{
	"CASESAFEID",
	"ISCHANGED",
	"PRIORVALUE",
	"ISNEW",
	"ISCLONE",
	"INCLUDES",
	"ISPICKVAL",
	"__SFPLUS__",
	`"CASE"`,
}

var toolingContractMarkers = []string{
	"FlowDefinition",
	"ProcessType",
	"TriggerType",
	"TriggerObjectOrEvent",
	"DeveloperName",
}

func TestAppBlindL0DoesNotImportProductDialectModules(t *testing.T) {
	root := repoRoot(t)
	for _, dir := range []string{
		"backend/cmd/runtime-api",
		"backend/internal",
		"backend/pkg",
		"shared",
	} {
		for _, file := range productionGoFilesRecursive(t, filepath.Join(root, dir)) {
			rel := toRepoRelative(t, root, file)
			fset := token.NewFileSet()
			parsed, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
			if err != nil {
				t.Fatalf("parse imports %s: %v", rel, err)
			}
			for _, imp := range parsed.Imports {
				importPath := strings.Trim(imp.Path.Value, `"`)
				for _, forbidden := range productDialectImportPrefixes {
					if importPath == forbidden || strings.HasPrefix(importPath, forbidden+"/") {
						t.Fatalf("%s imports product dialect module %q; app-blind L0 must depend on dialects only through distribution composition", rel, importPath)
					}
				}
			}
		}
	}
}

func TestAppBlindServerDependencyClosureDoesNotPullProductDialects(t *testing.T) {
	root := repoRoot(t)
	cmd := exec.Command("go", "list", "-deps", "./cmd/runtime-api")
	cmd.Dir = filepath.Join(root, "backend")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go list -deps ./cmd/runtime-api: %v\n%s", err, out)
	}
	for _, dep := range strings.Fields(string(out)) {
		for _, forbidden := range productDialectImportPrefixes {
			if dep == forbidden || strings.HasPrefix(dep, forbidden+"/") {
				t.Fatalf("cmd/runtime-api dependency closure includes product dialect %q; default server must stay app-blind", dep)
			}
		}
		if strings.HasSuffix(dep, "/internal/interfaces/dialect/salesforcev63") {
			t.Fatalf("cmd/runtime-api dependency closure includes retired in-tree Salesforce v63 dialect %q", dep)
		}
	}
}

func TestProductDistributionsDoNotImportLegacyRESTHandlerPackage(t *testing.T) {
	root := repoRoot(t)
	for _, file := range productionGoFilesRecursive(t, filepath.Join(root, "backend/distributions")) {
		rel := toRepoRelative(t, root, file)
		fset := token.NewFileSet()
		parsed, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
		if err != nil {
			t.Fatalf("parse imports %s: %v", rel, err)
		}
		for _, imp := range parsed.Imports {
			if strings.Trim(imp.Path.Value, `"`) == "github.com/nexuscrm/backend/internal/interfaces/rest" {
				t.Fatalf("%s imports the legacy top-level REST handler package; product route surfaces must own Salesforce wire handlers above app-blind L0", rel)
			}
		}
	}
}

func TestServiceDataLiteralsStayLedgered(t *testing.T) {
	root := repoRoot(t)
	want := ledgerCounts(t, "Service Data Literal Baseline")
	got := countMarkersByFile(t, root, []string{
		"backend",
		"shared",
		"nexuscrm-audit",
		"nexuscrm-kernel",
	}, []string{"/services/data"})
	assertCountMap(t, "service-data literal ledger", got, want)
}

func TestNeutralContractToolingShapeStaysLedgered(t *testing.T) {
	root := repoRoot(t)
	want := ledgerCountsAllowEmpty(t, "Tooling Contract Baseline")
	got := countMarkersByFile(t, root, []string{"shared/pkg/contract"}, toolingContractMarkers)
	assertCountMap(t, "Tooling-shaped neutral contract ledger", got, want)
}

func TestFormulaCompatibilityBranchesStayLedgered(t *testing.T) {
	root := repoRoot(t)
	want := ledgerCounts(t, "Formula Compatibility Branch Baseline")
	got := countMarkersByFile(t, root, []string{
		"nexuscrm-kernel/pkg/expression",
		"nexuscrm-kernel/pkg/formula",
		"nexuscrm-analytics",
	}, salesforceFormulaMarkers)
	assertCountMap(t, "formula compatibility branch ledger", got, want)
}

func ledgerCounts(t *testing.T, section string) map[string]int {
	t.Helper()
	return ledgerCountsWithEmptyPolicy(t, section, false)
}

func ledgerCountsAllowEmpty(t *testing.T, section string) map[string]int {
	t.Helper()
	return ledgerCountsWithEmptyPolicy(t, section, true)
}

func ledgerCountsWithEmptyPolicy(t *testing.T, section string, allowEmpty bool) map[string]int {
	t.Helper()
	root := repoRoot(t)
	body := readFile(t, filepath.Join(root, salesforceDerivedBoundaryLedger))
	lines := strings.Split(body, "\n")

	inSection := false
	out := map[string]int{}
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "## ") {
			if inSection {
				break
			}
			inSection = strings.TrimSpace(strings.TrimPrefix(trimmed, "## ")) == section
			continue
		}
		if !inSection || !strings.HasPrefix(trimmed, "| `") {
			continue
		}
		cells := markdownTableCells(trimmed)
		if len(cells) < 2 {
			t.Fatalf("%s section %q has malformed row %q", salesforceDerivedBoundaryLedger, section, line)
		}
		path := strings.Trim(cells[0], "` ")
		count, err := strconv.Atoi(strings.TrimSpace(cells[1]))
		if err != nil {
			t.Fatalf("%s section %q row for %s has invalid count %q: %v", salesforceDerivedBoundaryLedger, section, path, cells[1], err)
		}
		if path == "" {
			t.Fatalf("%s section %q contains empty path row", salesforceDerivedBoundaryLedger, section)
		}
		if _, exists := out[path]; exists {
			t.Fatalf("%s section %q contains duplicate path %s", salesforceDerivedBoundaryLedger, section, path)
		}
		out[path] = count
	}
	if !inSection && len(out) == 0 {
		t.Fatalf("%s missing section %q", salesforceDerivedBoundaryLedger, section)
	}
	if len(out) == 0 && !allowEmpty {
		t.Fatalf("%s section %q has no ledger rows", salesforceDerivedBoundaryLedger, section)
	}
	return out
}

func markdownTableCells(line string) []string {
	line = strings.TrimSpace(line)
	line = strings.TrimPrefix(line, "|")
	line = strings.TrimSuffix(line, "|")
	raw := strings.Split(line, "|")
	cells := make([]string, 0, len(raw))
	for _, cell := range raw {
		cells = append(cells, strings.TrimSpace(cell))
	}
	return cells
}

func countMarkersByFile(t *testing.T, root string, relDirs []string, markers []string) map[string]int {
	t.Helper()
	out := map[string]int{}
	for _, relDir := range relDirs {
		for _, file := range productionGoFilesRecursive(t, filepath.Join(root, relDir)) {
			rel := toRepoRelative(t, root, file)
			body := readFile(t, file)
			count := 0
			for _, marker := range markers {
				count += strings.Count(body, marker)
			}
			if count > 0 {
				out[rel] = count
			}
		}
	}
	return out
}

func countMarkersByExplicitFiles(t *testing.T, root string, relFiles []string, markers []string) map[string]int {
	t.Helper()
	out := map[string]int{}
	for _, rel := range relFiles {
		file := filepath.Join(root, filepath.FromSlash(rel))
		body := readFile(t, file)
		count := 0
		for _, marker := range markers {
			count += strings.Count(body, marker)
		}
		if count > 0 {
			out[rel] = count
		}
	}
	return out
}

func assertCountMap(t *testing.T, label string, got, want map[string]int) {
	t.Helper()
	if reflect.DeepEqual(got, want) {
		return
	}
	t.Fatalf("%s drifted.\n got:\n%s\nwant:\n%s\nUpdate %s only when the boundary expansion is intentional and assigned to a migration PR.",
		label, formatCountMap(got), formatCountMap(want), salesforceDerivedBoundaryLedger)
}

func formatCountMap(values map[string]int) string {
	var buf bytes.Buffer
	keys := make([]string, 0, len(values))
	for key := range values {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		buf.WriteString(key)
		buf.WriteByte(' ')
		buf.WriteString(strconv.Itoa(values[key]))
		buf.WriteByte('\n')
	}
	return strings.TrimRight(buf.String(), "\n")
}

func TestSalesforceDerivedBoundaryLedgerMentionsEveryMigrationPR(t *testing.T) {
	root := repoRoot(t)
	body := readFile(t, filepath.Join(root, salesforceDerivedBoundaryLedger))
	for _, pr := range []string{"PR1", "PR2", "PR3", "PR4", "PR5", "PR6"} {
		if !strings.Contains(body, pr) {
			t.Fatalf("%s does not mention %s; every planned migration PR must have an owner row", salesforceDerivedBoundaryLedger, pr)
		}
	}
}

func TestL0AGoldenBaselineInventoryHasNoInventoryOnlyClaims(t *testing.T) {
	root := repoRoot(t)
	body := readFile(t, filepath.Join(root, salesforceDerivedBoundaryLedger))
	lines := strings.Split(body, "\n")

	inSection := false
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "## ") {
			if inSection {
				break
			}
			inSection = strings.TrimSpace(strings.TrimPrefix(trimmed, "## ")) == "Golden Baseline Inventory"
			continue
		}
		if inSection && strings.Contains(trimmed, "inventory-only") {
			t.Fatalf("%s Golden Baseline Inventory contains unguarded L0-A claim %q; L0-A baselines must be live-test-backed before the stage is declared sealed", salesforceDerivedBoundaryLedger, trimmed)
		}
	}
	if !inSection {
		t.Fatalf("%s missing Golden Baseline Inventory section", salesforceDerivedBoundaryLedger)
	}
}

func TestServiceDataLiteralMatcherDocumentsSplitLiteralLimit(t *testing.T) {
	dir := t.TempDir()
	file := filepath.Join(dir, "sample.go")
	if err := os.WriteFile(file, []byte("package sample\n\nconst path = \"/services\" + \"/data\" + \"/v63.0/query\"\n"), 0o600); err != nil {
		t.Fatalf("write fixture: %v", err)
	}
	// This PR0 seal intentionally counts literal text in production files. The
	// fixture documents the current limit: split literals are handled by future
	// route-ownership migration tests, not this thin baseline guard.
	if got := countMarkersByFile(t, dir, []string{"."}, []string{"/services/data"}); len(got) != 0 {
		t.Fatalf("split literal fixture unexpectedly matched: %#v", got)
	}
}
