package api

import (
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"reflect"
	"strings"
	"testing"
	"unicode"
)

// Anti-regrowth seal for L0-E PR-1 (shared-module arm).
//
// The five deleted Salesforce-shaped REST DTOs (UserDTO, LoginRequest,
// LoginResponse, PermissionSetDTO, and the api-package SearchResult) were the
// only types in this app-blind package that carried PascalCase-first-letter
// json tags (e.g. `json:"Email"`, `json:"ObjectLabel"`). Every surviving,
// neutral DTO uses camelCase-first json tags (e.g. `json:"objectApiName"`).
//
// This test scans every struct field json tag declared in shared/pkg/api/*.go
// and fails if any tag name begins with an uppercase letter. That both proves
// the SF wire-shape DTOs are gone and prevents a new one from creeping back
// into this app-blind surface.
func TestNoPascalCaseJSONTagsInSharedAPI(t *testing.T) {
	entries, err := os.ReadDir(".")
	if err != nil {
		t.Fatalf("read package dir: %v", err)
	}

	fset := token.NewFileSet()
	scanned := 0

	for _, entry := range entries {
		name := entry.Name()
		if entry.IsDir() || !strings.HasSuffix(name, ".go") {
			continue
		}
		if strings.HasSuffix(name, "_test.go") {
			continue
		}

		file, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution)
		if err != nil {
			t.Fatalf("parse %s: %v", name, err)
		}
		scanned++

		ast.Inspect(file, func(n ast.Node) bool {
			field, ok := n.(*ast.Field)
			if !ok || field.Tag == nil {
				return true
			}
			// field.Tag.Value includes the surrounding backticks/quotes.
			raw, err := unquoteTag(field.Tag.Value)
			if err != nil {
				return true
			}
			jsonTag, ok := reflect.StructTag(raw).Lookup("json")
			if !ok {
				return true
			}
			jsonName := jsonTag
			if idx := strings.IndexByte(jsonName, ','); idx >= 0 {
				jsonName = jsonName[:idx]
			}
			if jsonName == "" || jsonName == "-" {
				return true
			}
			if r := []rune(jsonName)[0]; unicode.IsUpper(r) {
				pos := fset.Position(field.Tag.Pos())
				fieldName := "<embedded>"
				if len(field.Names) > 0 {
					fieldName = field.Names[0].Name
				}
				t.Errorf("%s: field %s has PascalCase json tag %q; "+
					"app-blind shared/pkg/api must use camelCase-first json tags "+
					"(the deleted Salesforce-shaped DTOs were the only PascalCase ones). "+
					"See L0-E PR-1.", pos, fieldName, jsonName)
			}
			return true
		})
	}

	if scanned == 0 {
		t.Fatal("no non-test .go files scanned in shared/pkg/api; seal would be vacuous")
	}
}

// unquoteTag strips the enclosing backticks (or double quotes) from a raw Go
// struct tag literal as it appears in source.
func unquoteTag(lit string) (string, error) {
	if len(lit) >= 2 {
		if (lit[0] == '`' && lit[len(lit)-1] == '`') ||
			(lit[0] == '"' && lit[len(lit)-1] == '"') {
			return lit[1 : len(lit)-1], nil
		}
	}
	return "", os.ErrInvalid
}
