package layoutownership

import (
	"bufio"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

func layoutOwnershipRepoRoot(t *testing.T) string {
	t.Helper()

	_, file, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("runtime.Caller(0) failed")
	}
	return filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", "..", ".."))
}

func jsonProjectionTag(field *ast.Field) string {
	if field.Tag == nil {
		return ""
	}
	raw, err := strconv.Unquote(field.Tag.Value)
	if err != nil {
		return ""
	}
	return reflect.StructTag(raw).Get("json")
}

func TestProjectionAllowlist_GoModels(t *testing.T) {
	allowlist := map[string]struct{}{
		"AppConfig.IsDefault":                   {},
		"ApprovalPageLayout.IsCustomized":       {},
		"ApprovalPageLayout.IsDefault":          {},
		"CampaignMemberStatus.IsDefault":        {},
		"ObjectMetadata.IsCustomized":           {},
		"PageLayout.IsCustomized":               {},
		"PageLayout.IsDefault":                  {},
		"ProfileAppVisibility.IsDefault":        {},
		"RecordType.IsDefault":                  {},
		"SystemActionFieldDefault.IsCustomized": {},
		"SystemActionInputGuard.IsCustomized":   {},
		"SystemActionSchema.IsCustomized":       {},
		"SystemApp.IsDefault":                   {},
		"SystemApprovalPageLayout.IsCustomized": {},
		"SystemApprovalPageLayout.IsDefault":    {},
		"SystemCustomPermission.IsCustomized":   {},
		"SystemField.IsCustomized":              {},
		"SystemObject.IsCustomized":             {},
		"SystemObjectFieldMapping.IsCustomized": {},
		"SystemPageLayout.IsCustomized":         {},
		"SystemPageLayout.IsDefault":            {},
		"SystemStandardAction.IsCustomized":     {},
		"SystemValidationRule.IsCustomized":     {},
		"ValidationRule.IsCustomized":           {},
	}

	targets := map[string]struct{}{
		"IsCustomized": {},
		"IsDefault":    {},
		"RouteSource":  {},
	}

	root := filepath.Join(layoutOwnershipRepoRoot(t), "shared", "pkg", "models")
	fset := token.NewFileSet()
	pkgs, err := parser.ParseDir(fset, root, func(info os.FileInfo) bool {
		return strings.HasSuffix(info.Name(), ".go") && !strings.HasSuffix(info.Name(), "_test.go")
	}, 0)
	if err != nil {
		t.Fatalf("parse shared/pkg/models: %v", err)
	}

	for _, pkg := range pkgs {
		for fileName, file := range pkg.Files {
			ast.Inspect(file, func(n ast.Node) bool {
				ts, ok := n.(*ast.TypeSpec)
				if !ok {
					return true
				}
				st, ok := ts.Type.(*ast.StructType)
				if !ok {
					return false
				}
				for _, field := range st.Fields.List {
					for _, name := range field.Names {
						if _, ok := targets[name.Name]; !ok {
							continue
						}
						key := ts.Name.Name + "." + name.Name
						tag := jsonProjectionTag(field)
						if strings.Split(tag, ",")[0] == "-" {
							continue
						}
						if _, ok := allowlist[key]; ok {
							continue
						}
						pos := fset.Position(field.Pos())
						t.Errorf("unallowlisted Go projection %s with json tag %q at %s:%d", key, tag, fileName, pos.Line)
					}
				}
				return false
			})
		}
	}
}

func TestProjectionAllowlist_TypeScriptGeneratedSchema(t *testing.T) {
	allowlist := map[string]struct{}{
		"CampaignMemberStatus.IsDefault":        {},
		"FlowDefinition.IsCustomized":           {},
		"ProfileAppVisibility.IsDefault":        {},
		"SystemActionFieldDefault.IsCustomized": {},
		"SystemActionInputGuard.IsCustomized":   {},
		"SystemActionSchema.IsCustomized":       {},
		"SystemApp.IsDefault":                   {},
		"SystemApprovalPageLayout.IsCustomized": {},
		"SystemApprovalPageLayout.IsDefault":    {},
		"SystemCustomPermission.IsCustomized":   {},
		"SystemField.IsCustomized":              {},
		"SystemObject.IsCustomized":             {},
		"SystemObjectFieldMapping.IsCustomized": {},
		"SystemPageLayout.IsCustomized":         {},
		"SystemPageLayout.IsDefault":            {},
		"SystemStandardAction.IsCustomized":     {},
		"SystemValidationRule.IsCustomized":     {},
	}

	targets := map[string]struct{}{
		"IsCustomized": {},
		"IsDefault":    {},
		"RouteSource":  {},
	}

	path := filepath.Join(layoutOwnershipRepoRoot(t), "frontend", "src", "generated-schema.ts")
	file, err := os.Open(path)
	if err != nil {
		t.Fatalf("open generated-schema.ts: %v", err)
	}
	defer file.Close()

	currentInterface := ""
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "export interface ") {
			fields := strings.Fields(line)
			if len(fields) >= 3 {
				currentInterface = fields[2]
			}
			continue
		}

		trimmed := strings.TrimSpace(line)
		for fieldName := range targets {
			prefix := fieldName + ":"
			optionalPrefix := fieldName + "?:"
			if !strings.HasPrefix(trimmed, prefix) && !strings.HasPrefix(trimmed, optionalPrefix) {
				continue
			}

			key := currentInterface + "." + fieldName
			if _, ok := allowlist[key]; ok {
				break
			}
			t.Errorf("unallowlisted TypeScript projection %s in %s", key, path)
			break
		}
	}
	if err := scanner.Err(); err != nil {
		t.Fatalf("scan generated-schema.ts: %v", err)
	}
}

func isAllowedOwnershipSQLPath(path string) bool {
	sep := string(filepath.Separator)
	return strings.HasSuffix(path, sep+"backend"+sep+"internal"+sep+"infrastructure"+sep+"persistence"+sep+"repo_layout.go") ||
		strings.HasSuffix(path, sep+"backend"+sep+"internal"+sep+"layoutrouting"+sep+"wildcard.go") ||
		strings.Contains(path, sep+"backend"+sep+"internal"+sep+"controlplane"+sep+"layoutownership"+sep)
}

func TestProjectionAllowlist_SinglePathSQLAssertion(t *testing.T) {
	root := filepath.Join(layoutOwnershipRepoRoot(t), "backend", "internal")

	err := filepath.Walk(root, func(path string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if info.IsDir() {
			return nil
		}
		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") || isAllowedOwnershipSQLPath(path) {
			return nil
		}

		fset := token.NewFileSet()
		file, err := parser.ParseFile(fset, path, nil, 0)
		if err != nil {
			return err
		}

		ast.Inspect(file, func(n ast.Node) bool {
			lit, ok := n.(*ast.BasicLit)
			if !ok || lit.Kind != token.STRING {
				return true
			}
			text, err := strconv.Unquote(lit.Value)
			if err != nil {
				return true
			}
			if strings.Contains(text, "_System_LayoutAssignment") && strings.Contains(text, "RouteSource") {
				pos := fset.Position(lit.Pos())
				t.Errorf("direct RouteSource SQL escaped single-path boundary at %s:%d", path, pos.Line)
			}
			if strings.Contains(text, "_System_PageLayout") && strings.Contains(text, "IsDefault") {
				pos := fset.Position(lit.Pos())
				t.Errorf("direct IsDefault SQL escaped single-path boundary at %s:%d", path, pos.Line)
			}
			return true
		})
		return nil
	})
	if err != nil {
		t.Fatalf("walk backend/internal: %v", err)
	}
}
