// Package validator provides a validator for the marka data.
package validator

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
	"sync"

	"git.max-richter.dev/max/marka/registry"
	"github.com/santhosh-tekuri/jsonschema/v6"
)

var (
	loadOnce    sync.Once
	compiler    *jsonschema.Compiler
	compileErr  error
	schemaCache sync.Map
)

// ValidateSchema validates instance against the Schema.org JSON Schema named `schemaName`.
// Examples: ValidateSchema(inst, "Recipe"), ValidateSchema(inst, "schema:Recipe").
func ValidateSchema(instance any, schemaName string) error {
	if err := ensureCompiler(); err != nil {
		return err
	}

	ref := normalizeRef(schemaName)

	if v, ok := schemaCache.Load(ref); ok {
		if err := v.(*jsonschema.Schema).Validate(instance); err != nil {
			return fmt.Errorf("validation failed: %w", err)
		}
		return nil
	}

	sch, err := compiler.Compile(ref)
	if err != nil {
		return fmt.Errorf("failed to compile schema %q: %w", ref, err)
	}
	schemaCache.Store(ref, sch)

	if err := sch.Validate(instance); err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}
	return nil
}

func ensureCompiler() error {
	loadOnce.Do(func() {
		c := jsonschema.NewCompiler()

		rawSchemas, err := registry.GetSchemas()
		if err != nil {
			compileErr = fmt.Errorf("read schema directory: %w", err)
			return
		}

		for _, rawSchema := range rawSchemas {
			js, err := jsonschema.UnmarshalJSON(bytes.NewReader(rawSchema))
			if err != nil {
				compileErr = err
				return
			}

			id := extractID(rawSchema)
			if err := c.AddResource(id, js); err != nil {
				compileErr = fmt.Errorf("add resource %s: %w", id, err)
				return
			}
		}

		compiler = c
	})
	return compileErr
}

func extractID(raw []byte) string {
	var tmp struct {
		ID string `json:"$id"`
	}
	_ = json.Unmarshal(raw, &tmp)
	return strings.TrimSpace(tmp.ID)
}

func normalizeRef(name string) string {
	n := strings.TrimSpace(name)
	if strings.HasPrefix(n, "schema:") || strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
		return n
	}
	return "schema:" + n
}
