{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "TwinFyRx suspecting_rule.rule_logic",
  "description": "One JSON Schema branch per rule_category value. The rule_executor.py in the SDK translates any valid rule_logic into executable SQL or evaluates it in-memory. Codes ending in '*' are ICD-10 prefix patterns (LIKE match); ATC codes are prefix-matched against the ndc_atc crosswalk.",
  "oneOf": [
    {
      "$comment": "rule_category = prescriber_specialty, operator = switch",
      "title": "prescriber_specialty — specialty-to-diagnosis routing",
      "description": "Routes a claim toward different suspected ICD-10 targets based on the prescribing specialty. Each branch maps one or more specialty values to a list of candidate diagnoses. The executor renders a UNION ALL query, one branch per set of specialties.",
      "type": "object",
      "required": ["operator", "field", "branches"],
      "additionalProperties": false,
      "properties": {
        "operator": { "type": "string", "const": "switch" },
        "field":    { "type": "string", "const": "prescriber_specialty" },
        "branches": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "required": ["values", "target_icd10"],
            "properties": {
              "values":       { "type": "array", "items": { "type": "string" }, "description": "Prescriber specialty strings (exact match, case-sensitive)." },
              "target_icd10": { "type": "array", "items": { "type": "string" }, "description": "Suspected ICD-10 codes for this specialty group. Use the first element as the primary target." },
              "confidence":   { "type": "number", "minimum": 0, "maximum": 1, "description": "Per-branch confidence override. Falls back to the rule-level confidence field if absent." }
            }
          }
        }
      },
      "examples": [
        {
          "operator": "switch",
          "field": "prescriber_specialty",
          "branches": [
            { "values": ["Rheumatology", "Immunology"], "target_icd10": ["M05.9"], "confidence": 0.85 },
            { "values": ["Dermatology"], "target_icd10": ["L40.50"], "confidence": 0.72 }
          ]
        }
      ]
    },
    {
      "$comment": "rule_category = patient_age",
      "title": "patient_age — age-based prior or exclusion",
      "description": "A single field comparison or an AND composite restricting by patient age at fill date. The field patient_age_years resolves to members.age_at_fill in the claims schema. Supports BETWEEN, >=, <=, >, <.",
      "type": "object",
      "required": ["field", "operator", "value"],
      "additionalProperties": false,
      "properties": {
        "field":    { "type": "string", "const": "patient_age_years" },
        "operator": { "type": "string", "enum": ["=", "!=", ">=", "<=", ">", "<", "BETWEEN"] },
        "value": {
          "oneOf": [
            { "type": "number", "description": "Single age threshold." },
            { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2, "description": "For BETWEEN: [lower, upper] inclusive." }
          ]
        }
      },
      "examples": [
        { "field": "patient_age_years", "operator": "BETWEEN", "value": [18, 65] },
        { "field": "patient_age_years", "operator": ">=", "value": 50 }
      ]
    },
    {
      "$comment": "rule_category = patient_gender",
      "title": "patient_gender — gender-based prior",
      "description": "Restricts suspects to a specific patient gender. Usually compounded with age (see compound category).",
      "type": "object",
      "required": ["field", "operator", "value"],
      "additionalProperties": false,
      "properties": {
        "field":    { "type": "string", "const": "patient_gender" },
        "operator": { "type": "string", "enum": ["=", "!="] },
        "value":    { "type": "string", "enum": ["M", "F"], "description": "'M' or 'F'." }
      },
      "examples": [
        { "field": "patient_gender", "operator": "=", "value": "F" }
      ]
    },
    {
      "$comment": "rule_category = concurrent_rx",
      "title": "concurrent_rx — co-prescribing signal",
      "description": "Fires when the member has (or does not have) concurrent fills in a given ATC therapeutic class within a lookback window. ATC codes are prefix-matched against the ndc_atc crosswalk (e.g. C07A matches all beta-blockers). match_type 'none' inverts the predicate (NOT EXISTS).",
      "type": "object",
      "required": ["operator", "atc_classes"],
      "additionalProperties": false,
      "properties": {
        "operator":     { "type": "string", "const": "concurrent_rx" },
        "atc_classes":  { "type": "array", "items": { "type": "string" }, "minItems": 1, "description": "ATC level-3 or level-4 prefix codes (e.g. 'C07A', 'M05BA')." },
        "lookback_days":{ "type": "integer", "minimum": 1, "default": 90, "description": "Days before the index Rx fill to search for concurrent fills." },
        "match_type":   { "type": "string", "enum": ["any", "all", "none"], "default": "any", "description": "'any' = at least one ATC class present; 'all' = all classes present; 'none' = none present (exclusion rule)." }
      },
      "examples": [
        {
          "operator": "concurrent_rx",
          "atc_classes": ["C07A", "C09A"],
          "lookback_days": 90,
          "match_type": "any"
        },
        {
          "operator": "concurrent_rx",
          "atc_classes": ["A10B"],
          "lookback_days": 180,
          "match_type": "none"
        }
      ]
    },
    {
      "$comment": "rule_category = medical_claims_history",
      "title": "medical_claims_history — prior diagnosis or procedure",
      "description": "Fires when the member has a matching diagnosis code or procedure code in the medical claims lookback window. ICD-10 codes ending in '*' are treated as prefix patterns (e.g. 'M32.*' matches all Systemic Lupus codes).",
      "type": "object",
      "required": ["operator", "match"],
      "additionalProperties": false,
      "properties": {
        "operator":     { "type": "string", "const": "claims_history" },
        "match": {
          "type": "object",
          "required": ["type", "codes"],
          "properties": {
            "type": {
              "type": "string",
              "enum": ["diagnosis_code", "procedure_code", "diagnosis_or_procedure"],
              "description": "What to search for in medical claims."
            },
            "codes": {
              "type": "array",
              "items": { "type": "string" },
              "minItems": 1,
              "description": "ICD-10-CM codes (optionally ending in '*' for prefix match) or CPT procedure codes."
            }
          }
        },
        "lookback_days": { "type": "integer", "minimum": 1, "default": 365, "description": "Days before the index Rx fill to search for prior medical claims." }
      },
      "examples": [
        {
          "operator": "claims_history",
          "match": { "type": "diagnosis_code", "codes": ["M05.9", "M32.*"] },
          "lookback_days": 365
        },
        {
          "operator": "claims_history",
          "match": { "type": "procedure_code", "codes": ["96413", "96415"] },
          "lookback_days": 180
        }
      ]
    },
    {
      "$comment": "rule_category = compound",
      "title": "compound — multi-signal composite",
      "description": "Combines two or more conditions from different signal sources using AND / OR logic. Each condition in the array may itself be a field comparison, concurrent_rx, or claims_history node. Nesting is supported for complex compound rules.",
      "type": "object",
      "required": ["operator", "conditions"],
      "additionalProperties": false,
      "properties": {
        "operator": { "type": "string", "enum": ["AND", "OR"] },
        "conditions": {
          "type": "array",
          "minItems": 2,
          "items": {
            "type": "object",
            "description": "A field comparison, concurrent_rx node, or claims_history node. May be nested AND/OR."
          }
        }
      },
      "examples": [
        {
          "operator": "AND",
          "conditions": [
            { "field": "patient_gender", "operator": "=", "value": "F" },
            { "field": "patient_age_years", "operator": ">=", "value": 55 }
          ]
        },
        {
          "operator": "AND",
          "conditions": [
            { "operator": "concurrent_rx", "atc_classes": ["L04A"], "lookback_days": 90, "match_type": "any" },
            { "operator": "claims_history", "match": { "type": "diagnosis_code", "codes": ["M45.*"] }, "lookback_days": 365 }
          ]
        }
      ]
    },
    {
      "$comment": "rule_category = single_indication",
      "title": "single_indication — drug-wide rule with no conditions",
      "description": "Used when a drug maps to exactly one clinical indication with very high confidence (is_single_indication_drug = true). The rule logic is empty — all fills of this drug are suspected for the associated ICD-10 code regardless of claims context.",
      "type": "object",
      "maxProperties": 0,
      "examples": [{}]
    }
  ]
}
