{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://abyss.keyboard-hub.com/schemas/module-definition/v1.json",
  "title": "KeyboardModuleDefinition",
  "description": "A non-key hardware module definition for physical preview rendering and keymap capability discovery.",
  "type": "object",
  "required": ["name", "type", "custom_attrs"],
  "additionalProperties": false,
  "allOf": [
    {
      "if": {
        "properties": { "visual_type": { "const": "none" } },
        "required": ["visual_type"]
      },
      "then": {
        "not": {
          "properties": { "visual_attr": {} },
          "required": ["visual_attr"]
        }
      },
      "else": {
        "properties": { "visual_attr": {} },
        "required": ["visual_attr"]
      }
    },
    {
      "if": {
        "properties": { "visual_type": { "const": "override" } },
        "required": ["visual_type"]
      },
      "then": {
        "properties": {
          "visual_attr": { "$ref": "#/definitions/OverrideVisual" }
        }
      }
    }
  ],
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "description": "Stable module instance identifier."
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "description": "Human-readable module name for UI display."
    },
    "type": {
      "type": "string",
      "enum": ["trackball", "rotary_encoder", "touch_pad", "custom"],
      "description": "Semantic module type. Functional values are configured by keymaps."
    },
    "visual_type": {
      "type": "string",
      "enum": [
        "trackball",
        "rotary_encoder",
        "touch_pad",
        "circle",
        "rectangle",
        "override",
        "none"
      ],
      "description": "Renderer used for keyboard previews. Defaults to type when the type has a matching visual, otherwise circle."
    },
    "visual_attr": {
      "description": "Visual-only rendering attributes.",
      "anyOf": [
        { "$ref": "#/definitions/TrackballVisual" },
        { "$ref": "#/definitions/RotaryEncoderVisual" },
        { "$ref": "#/definitions/CircleVisual" },
        { "$ref": "#/definitions/RectangleVisual" },
        { "$ref": "#/definitions/OverrideVisual" }
      ]
    },
    "custom_attrs": {
      "type": "array",
      "description": "Custom functional attributes exposed by this module. Actual values live in keymap modules.",
      "items": { "$ref": "#/definitions/CustomAttribute" },
      "default": []
    },
    "firmwareDialect": {
      "$ref": "#/definitions/ModuleDialect",
      "description": "Optional source firmware metadata.",
      "default": {}
    }
  },
  "definitions": {
    "TrackballVisual": {
      "type": "object",
      "required": ["x", "y"],
      "additionalProperties": false,
      "properties": {
        "size": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "x": { "type": "number" },
        "y": { "type": "number" }
      }
    },
    "RotaryEncoderVisual": {
      "type": "object",
      "required": ["x", "y"],
      "additionalProperties": false,
      "properties": {
        "size": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "x": { "type": "number" },
        "y": { "type": "number" }
      }
    },
    "CircleVisual": {
      "type": "object",
      "required": ["x", "y"],
      "additionalProperties": false,
      "properties": {
        "size": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "x": { "type": "number" },
        "y": { "type": "number" }
      }
    },
    "RectangleVisual": {
      "type": "object",
      "required": ["x", "y"],
      "additionalProperties": false,
      "properties": {
        "x": { "type": "number" },
        "y": { "type": "number" },
        "w": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "h": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "r": { "type": "number" },
        "rx": { "type": "number" },
        "ry": { "type": "number" }
      }
    },
    "OverrideVisual": {
      "type": "object",
      "required": ["targetModuleName", "layers", "override"],
      "additionalProperties": false,
      "properties": {
        "targetModuleName": {
          "type": "string",
          "minLength": 1,
          "description": "Module name whose visual is overridden."
        },
        "layers": {
          "type": "array",
          "items": { "type": "integer", "minimum": 0 },
          "description": "Layer indexes where this visual override applies."
        },
        "override": {
          "description": "Replacement visual attributes.",
          "anyOf": [
            { "$ref": "#/definitions/TrackballVisual" },
            { "$ref": "#/definitions/RotaryEncoderVisual" },
            { "$ref": "#/definitions/CircleVisual" },
            { "$ref": "#/definitions/RectangleVisual" }
          ]
        }
      }
    },
    "ModuleDialect": {
      "type": "object",
      "description": "Firmware-specific module metadata. Custom dialect keys may contain any object.",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": true
      },
      "properties": {
        "zmk": { "type": "object", "additionalProperties": true },
        "via": { "type": "object", "additionalProperties": true },
        "vial": { "type": "object", "additionalProperties": true }
      }
    },
    "CustomAttribute": {
      "type": "object",
      "required": ["name", "type"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable attribute key."
        },
        "type": {
          "type": "string",
          "enum": ["number", "integer", "boolean", "string"],
          "description": "Value type for editor controls and validation."
        },
        "label": { "type": "string", "minLength": 1 },
        "description": { "type": "string", "minLength": 1 },
        "required": { "type": "boolean", "default": false },
        "layerWise": {
          "type": "boolean",
          "default": false,
          "description": "Whether this attribute value can be overridden per keymap layer."
        },
        "default": {
          "description": "Default value for this attribute.",
          "type": ["string", "number", "boolean", "null"]
        },
        "constraints": {
          "type": "array",
          "default": [],
          "items": { "$ref": "#/definitions/Constraint" }
        }
      }
    },
    "Constraint": {
      "oneOf": [
        { "$ref": "#/definitions/RangeConstraint" },
        { "$ref": "#/definitions/ValuesConstraint" },
        { "const": "color" },
        { "const": "keybinding" },
        { "const": "layer" }
      ]
    },
    "RangeConstraint": {
      "type": "object",
      "required": ["min", "max"],
      "additionalProperties": false,
      "properties": { "min": { "type": "number" }, "max": { "type": "number" } }
    },
    "ValuesConstraint": {
      "type": "object",
      "required": ["values", "labels"],
      "additionalProperties": false,
      "properties": {
        "values": {
          "type": "array",
          "items": { "type": ["string", "number"] }
        },
        "labels": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 }
        }
      }
    }
  }
}
