{
  "resourceType": "StructureDefinition",
  "id": "gap-patient-profile",
  "url": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile",
  "version": "1.0.0",
  "name": "GapPatient",
  "title": "GAP Patient Profile",
  "status": "draft",
  "description": "A FHIR Patient profile for GAP requests.",
  "fhirVersion": "4.0.1",
  "kind": "resource",
  "abstract": false,
  "type": "Patient",
  "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Patient",
  "derivation": "constraint",
  "differential": {
    "element": [
      {
        "id": "Patient",
        "path": "Patient",
        "constraint": [
          {
            "key": "gap-nam-expiration-required",
            "severity": "error",
            "human": "HIN is required to have an expiration date when a value is present.",
            "expression": "identifier.where(system='https://ramq.gouv.qc.ca/id/nam' and value.exists()).exists() implies identifier.where(system='https://ramq.gouv.qc.ca/id/nam').period.end.exists()",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-nam-format",
            "severity": "error",
            "human": "HIN (NAM) must follow the format of 4 letters followed by 8 digits.",
            "expression": "identifier.where(system='https://ramq.gouv.qc.ca/id/nam' and value.exists()).all(value.matches('^[A-Z]{4}[0-9]{8}$'))",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-postal-code-format",
            "severity": "error",
            "human": "Postal code must follow Canadian format A1A1A1 (e.g., K1A0B1).",
            "expression": "address.postalCode.matches('^[A-Z][0-9][A-Z][0-9][A-Z][0-9]$')",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-patient-at-least-one-telecom",
            "severity": "error",
            "human": "At least one of phone or email must be present.",
            "expression": "telecom.where(system='phone').exists() or telecom.where(system='email').exists()",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-patient-no-sms-telecom",
            "severity": "error",
            "human": "SMS is not a permitted telecom system. Only phone and email are allowed.",
            "expression": "telecom.where(system='sms').empty()",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-patient-phone-number-format",
            "severity": "error",
            "human": "Phone number must contain exactly 10 digits.",
            "expression": "telecom.where(system='phone').all(value.matches('^[0-9]{10}$'))",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          },
          {
            "key": "gap-patient-email-format",
            "severity": "error",
            "human": "Email must follow a strict valid email format without consecutive dots, leading/trailing hyphens in domain parts, or other invalid patterns.",
            "expression": "telecom.where(system='email').all(value.matches('^[a-zA-Z0-9][a-zA-Z0-9._%+-]*[a-zA-Z0-9]@[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\\\\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\\\\.[a-zA-Z]{2,}$') or value.matches('^[a-zA-Z0-9]@[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\\\\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\\\\.[a-zA-Z]{2,}$'))",
            "source": "http://akinox.com/fhir/StructureDefinition/gap-patient-profile"
          }
        ]
      },
      {
        "id": "Patient.identifier",
        "path": "Patient.identifier",
        "slicing": {
          "discriminator": [
            {
              "type": "value",
              "path": "system"
            }
          ],
          "rules": "open"
        }
      },
      {
        "id": "Patient.identifier:nam",
        "path": "Patient.identifier",
        "sliceName": "nam",
        "min": 0,
        "max": "1"
      },
      {
        "id": "Patient.identifier:nam.system",
        "path": "Patient.identifier.system",
        "min": 1,
        "patternUri": "https://ramq.gouv.qc.ca/id/nam"
      },
      {
        "id": "Patient.identifier:nam.period",
        "path": "Patient.identifier.period",
        "min": 1
      },
      {
        "id": "Patient.identifier:nam.period.end",
        "path": "Patient.identifier.period.end",
        "min": 1
      },
      {
        "id": "Patient.name",
        "path": "Patient.name",
        "min": 1,
        "max": "1"
      },
      {
        "id": "Patient.name.family",
        "path": "Patient.name.family",
        "min": 1
      },
      {
        "id": "Patient.name.given",
        "path": "Patient.name.given",
        "min": 1,
        "max": "1"
      },
      {
        "id": "Patient.telecom",
        "path": "Patient.telecom",
        "slicing": {
          "discriminator": [
            {
              "type": "value",
              "path": "system"
            }
          ],
          "rules": "open"
        },
        "min": 1
      },
      {
        "id": "Patient.telecom:phone",
        "path": "Patient.telecom",
        "sliceName": "phone",
        "min": 0,
        "max": "1"
      },
      {
        "id": "Patient.telecom:phone.system",
        "path": "Patient.telecom.system",
        "min": 1,
        "patternCode": "phone"
      },
      {
        "id": "Patient.telecom:phone.value",
        "path": "Patient.telecom.value",
        "min": 1
      },
      {
        "id": "Patient.telecom:phone.use",
        "path": "Patient.telecom.use",
        "min": 1,
        "binding": {
          "strength": "required",
          "valueSet": "http://akinox.com/fhir/ValueSet/gap-phone-telecom-use-valueset"
        }
      },
      {
        "id": "Patient.telecom:phone.rank",
        "path": "Patient.telecom.rank",
        "min": 1
      },
      {
        "id": "Patient.telecom:email",
        "path": "Patient.telecom",
        "sliceName": "email",
        "min": 0,
        "max": "1"
      },
      {
        "id": "Patient.telecom:email.system",
        "path": "Patient.telecom.system",
        "min": 1,
        "patternCode": "email"
      },
      {
        "id": "Patient.telecom:email.use",
        "path": "Patient.telecom.use",
        "min": 1,
        "binding": {
          "strength": "required",
          "valueSet": "http://akinox.com/fhir/ValueSet/gap-email-telecom-use-valueset"
        }
      },
      {
        "id": "Patient.telecom:email.rank",
        "path": "Patient.telecom.rank",
        "min": 1
      },
      {
        "id": "Patient.gender",
        "path": "Patient.gender",
        "min": 1,
        "binding": {
          "strength": "required",
          "valueSet": "http://akinox.com/fhir/ValueSet/gap-gender-valueset"
        }
      },
      {
        "id": "Patient.birthDate",
        "path": "Patient.birthDate",
        "min": 1
      },
      {
        "id": "Patient.address",
        "path": "Patient.address",
        "min": 1
      },
      {
        "id": "Patient.address.postalCode",
        "path": "Patient.address.postalCode",
        "min": 1
      },
      {
        "id": "Patient.communication",
        "path": "Patient.communication",
        "min": 1
      },
      {
        "id": "Patient.communication.language",
        "path": "Patient.communication.language",
        "binding": {
          "strength": "required",
          "valueSet": "http://akinox.com/fhir/ValueSet/gap-language-valueset"
        }
      },
      {
        "id": "Patient.communication.preferred",
        "path": "Patient.communication.preferred",
        "min": 1,
        "patternBoolean": true
      }
    ]
  }
}