{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.tiantianspeak.com/docs/schemas/task.schema.json",
  "title": "TianTianSpeak task",
  "description": "一个任务文件 = 一个任务（链）。steps 与 stages 二选一：只有 steps 等价于一个只含这些步骤的阶段。",
  "type": "object",
  "required": ["id", "name"],
  "oneOf": [
    {"required": ["steps"], "not": {"required": ["stages"]}},
    {"required": ["stages"], "not": {"required": ["steps"]}}
  ],
  "properties": {
    "id": {"type": "string", "minLength": 1, "pattern": "^[A-Za-z0-9._-]+$", "description": "任务 id，同 id 重新导入是替换；不要以 custom- 开头（留给 App 里新建的自定义任务）"},
    "name": {"type": "string", "minLength": 1},
    "description": {"type": "string", "description": "任务卡片上的说明，可用 \\n 换行"},
    "minAppVersion": {"type": "string", "pattern": "^\\d+(\\.\\d+)*$", "description": "能用这个任务的最低 App 版本，如 0.5.18；用到较新的能力（如 sameArticle）时要写"},
    "ageGroups": {"type": "array", "items": {"$ref": "#/$defs/ageGroup"}, "description": "只对这些年龄段显示；不写 = 所有人"},
    "order": {"enum": ["sequence", "any"], "default": "sequence", "description": "sequence 严格按顺序解锁；any 任意顺序"},
    "badge": {
      "type": "object",
      "properties": {
        "id": {"type": "string"},
        "name": {"type": "string"},
        "icon": {"type": "string", "description": "SF Symbol 名；不存在时用默认图标"},
        "description": {"type": "string"}
      },
      "additionalProperties": false
    },
    "bankSets": {
      "type": "object",
      "description": "题库集合：键是名字，供步骤的 target.bankSet 引用",
      "additionalProperties": {
        "oneOf": [
          {"type": "array", "items": {"type": "string"}, "description": "所有人相同的题库 id 列表"},
          {"type": "object", "propertyNames": {"$ref": "#/$defs/ageGroup"}, "additionalProperties": {"type": "array", "items": {"type": "string"}}, "description": "按年龄段给题库 id 列表"}
        ]
      }
    },
    "steps": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/step"}},
    "stages": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/stage"}}
  },
  "$defs": {
    "ageGroup": {"enum": ["kindergarten", "primary", "middle", "high", "adult"]},
    "stage": {
      "type": "object",
      "required": ["steps"],
      "properties": {
        "id": {"type": "string", "description": "同一任务内不可重复"},
        "name": {"type": "string"},
        "daily": {"type": "boolean", "default": false, "description": "true：阶段里的全部步骤每天都要达标，累计 days 个达标日（不要求连续）"},
        "days": {"type": "integer", "minimum": 1, "description": "只能和 daily: true 一起用"},
        "steps": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/step"}}
      },
      "if": {"properties": {"daily": {"const": true}}, "required": ["daily"]},
      "then": {"required": ["days"]},
      "else": {"not": {"required": ["days"]}},
      "additionalProperties": false
    },
    "step": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": {"enum": ["readAloud", "retell", "speak"], "description": "模块：朗诵 / 复述 / 口述（diary 是 speak 的旧别名）"},
        "mode": {
          "description": "模块下的模式；不写 = 该模块任意模式（朗诵不写时只含 read、recite，不含 typing、dictation）。可写数组，任一模式即可",
          "oneOf": [{"$ref": "#/$defs/mode"}, {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/mode"}}]
        },
        "style": {"enum": ["introNormal", "introInterview", "reportWork", "reportReview"], "description": "仅 speak 的 intro / report；与 mode 矛盾时报错"},
        "times": {"type": "integer", "minimum": 1, "default": 1, "description": "需要达标几次（daily 阶段里是每天几次）"},
        "minScore": {"type": "integer", "minimum": 0, "maximum": 100},
        "minChars": {
          "description": "转写字数下限；可按年龄段分别给，没写的年龄段不限",
          "oneOf": [
            {"type": "integer", "minimum": 0},
            {"type": "object", "propertyNames": {"$ref": "#/$defs/ageGroup"}, "additionalProperties": {"type": "integer", "minimum": 0}}
          ]
        },
        "minDurationSeconds": {"type": "integer", "minimum": 0},
        "minAccuracy": {"type": "integer", "minimum": 0, "maximum": 100, "description": "正确率下限，仅打字 / 默写"},
        "maxSeconds": {"type": "integer", "minimum": 1, "description": "用时上限，仅打字 / 默写"},
        "sameArticle": {"type": "boolean", "description": "times 次必须是同一篇文章；需要 minAppVersion ≥ 0.5.18"},
        "target": {
          "type": "object",
          "properties": {
            "bank": {"type": "string", "description": "题库 id；不写 title 表示该题库里任意一篇"},
            "title": {"type": "string", "description": "条目标题；写了必须同时写 bank"},
            "bankSet": {"type": "string", "description": "bankSets 里的集合名；与 bank 只能写一个"},
            "topic": {"type": "string"}
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "mode": {"enum": ["read", "recite", "typing", "dictation", "normal", "brief", "structured", "guided", "diary", "topic", "intro", "report", "review"]}
  },
  "additionalProperties": false
}
