"""Minimaler JSON-Schema-Prüfer für die in docs/bridge-protokoll.schema.json
benutzte Teilmenge (type, enum, const, required, properties,
additionalProperties=false, items, maxItems, pattern, minimum, maxLength,
oneOf, $ref). Nur Standardbibliothek; wirft bei unbekannten Schlüsselwörtern,
damit das Schema nicht still über den Prüfer hinauswächst."""
import json
import re
from pathlib import Path

SCHEMA_PATH = Path(__file__).resolve().parents[2] / "docs" / "bridge-protokoll.schema.json"
KNOWN = {"$schema", "$id", "title", "description", "type", "enum", "const", "required", "properties",
         "additionalProperties", "items", "maxItems", "pattern", "minimum", "maxLength", "oneOf", "$ref", "$defs"}
TYPES = {"object": dict, "array": list, "string": str, "boolean": bool, "null": type(None)}


def load_schema() -> dict:
    return json.loads(SCHEMA_PATH.read_text(encoding="utf-8"))


def _type_ok(value, name) -> bool:
    if name == "integer":
        return isinstance(value, int) and not isinstance(value, bool)
    if name == "number":
        return isinstance(value, (int, float)) and not isinstance(value, bool)
    return isinstance(value, TYPES[name])


def errors(value, schema, root, path="$") -> list[str]:
    unknown = set(schema) - KNOWN
    if unknown:
        raise ValueError(f"unsupported schema keywords {unknown} at {path}")
    if "$ref" in schema:
        target = root
        for part in schema["$ref"].lstrip("#/").split("/"):
            target = target[part]
        return errors(value, target, root, path)
    if "oneOf" in schema:
        matches = [s for s in schema["oneOf"] if not errors(value, s, root, path)]
        return [] if len(matches) == 1 else [f"{path}: oneOf matched {len(matches)}"]
    out = []
    if "type" in schema:
        names = schema["type"] if isinstance(schema["type"], list) else [schema["type"]]
        if not any(_type_ok(value, n) for n in names):
            return [f"{path}: type {type(value).__name__} not in {names}"]
    if "const" in schema and value != schema["const"]:
        out.append(f"{path}: const")
    if "enum" in schema and value not in schema["enum"]:
        out.append(f"{path}: enum {value!r}")
    if isinstance(value, str):
        if "pattern" in schema and not re.search(schema["pattern"], value):
            out.append(f"{path}: pattern")
        if "maxLength" in schema and len(value) > schema["maxLength"]:
            out.append(f"{path}: maxLength")
    if isinstance(value, int) and not isinstance(value, bool) and "minimum" in schema and value < schema["minimum"]:
        out.append(f"{path}: minimum")
    if isinstance(value, dict):
        for key in schema.get("required", []):
            if key not in value:
                out.append(f"{path}: missing {key}")
        props = schema.get("properties", {})
        if schema.get("additionalProperties") is False:
            for key in value:
                if key not in props:
                    out.append(f"{path}: additional {key}")
        for key, sub in props.items():
            if key in value:
                out.extend(errors(value[key], sub, root, f"{path}.{key}"))
    if isinstance(value, list):
        if "maxItems" in schema and len(value) > schema["maxItems"]:
            out.append(f"{path}: maxItems")
        for index, item in enumerate(value):
            out.extend(errors(item, schema.get("items", {}), root, f"{path}[{index}]"))
    return out


def validate(value, definition: str) -> list[str]:
    root = load_schema()
    return errors(value, root["$defs"][definition], root)
