package register

import (
	"encoding/json"
	"io/fs"
	"sort"
	"strconv"
	"strings"
	"testing"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
)

func TestRegisteredResponseSchemasRequireEveryObjectProperty(t *testing.T) {
	assets := llm.NewAssetRegistry()
	if err := Register(completeRegistries(), assets); err != nil {
		t.Fatal(err)
	}
	schemaFS, err := assets.SchemaFS()
	if err != nil {
		t.Fatal(err)
	}

	count := 0
	err = fs.WalkDir(schemaFS, ".", func(path string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if entry.IsDir() || !strings.HasSuffix(path, ".json") {
			return nil
		}
		content, err := fs.ReadFile(schemaFS, path)
		if err != nil {
			return err
		}
		var schema any
		if err := json.Unmarshal(content, &schema); err != nil {
			t.Errorf("decode response schema %q: %v", path, err)
			return nil
		}
		count++
		assertRequiredObjectProperties(t, path, "$", schema)
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}
	if count == 0 {
		t.Fatal("no registered response schemas found")
	}
}

func assertRequiredObjectProperties(t *testing.T, schemaPath, nodePath string, value any) {
	t.Helper()
	switch node := value.(type) {
	case map[string]any:
		if properties, ok := node["properties"].(map[string]any); ok {
			required := make(map[string]struct{})
			if values, ok := node["required"].([]any); ok {
				for _, value := range values {
					if name, ok := value.(string); ok {
						required[name] = struct{}{}
					}
				}
			}
			propertyNames := make([]string, 0, len(properties))
			for name := range properties {
				propertyNames = append(propertyNames, name)
			}
			sort.Strings(propertyNames)
			for _, name := range propertyNames {
				if _, ok := required[name]; !ok {
					t.Errorf("response schema %q object %s declares property %q without requiring it", schemaPath, nodePath, name)
				}
			}
		}
		for name, child := range node {
			assertRequiredObjectProperties(t, schemaPath, nodePath+"."+name, child)
		}
	case []any:
		for index, child := range node {
			assertRequiredObjectProperties(t, schemaPath, nodePath+"["+strconv.Itoa(index)+"]", child)
		}
	}
}
