// PolarisCorePatient.fsh
// Defines the generic Polaris Patient profile 
// based on FHIR R4 and CA Core+.

Alias: $PolarisCorePatient = https://fhir.apps.health/StructureDefinition/polaris-core-patient


// patient self identifier rule
// patient identifier must have at least one identifier whose system matches the Polaris patient identifier pattern
Invariant: patient-has-polaris-id
Description: "Must have at least one identifier whose system matches the Polaris EMR pattern."
Severity: #error
Expression: "identifier.exists(system.matches('^https://fhir.apps.health/NamingSystem/[a-zA-Z0-9.-]+-patient-identifier$'))"

// patient name constraint - must have at least one name with family or given
Invariant: patient-name-family-or-given
Description: "Patient must have at least one name with either a family or given name."
Severity: #error
Expression: "name.exists(family.exists() or given.exists())"

// Profile Definition
Profile:        PolarisCorePatient
Parent:         Patient
Id:             polaris-core-patient
Title:          "Polaris Patient Core"
Description:    "Generic Polaris core profile for patient demographic and administrative information, based on CA Core+. Designed to support a broad range of integration needs."
* ^url = $PolarisCorePatient
* ^status = #draft
* obeys patient-has-polaris-id
* obeys patient-name-family-or-given

* . ^short      = "Polaris Patient Core"
* . ^definition = """
  Represents patient demographic and administrative information, capturing key details
  like identifiers, name, gender, birth date, and contact information using standard
  Polaris references. Uses instance-specific identifiers for tracking patients
  across systems.
  """

* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated.  This tells you which EMR instance (recommend using the instance identifier), and the version of the pipeline code/transformations."

// optional id
* id 0..1 MS
* id ^short = "Logical id of this artifact"
* id ^definition = "The logical id of the resource, used to reference this Patient resource in transactions and other operations. Assigned by the server and should not be changed by clients."

// Identifier requirements with slicing
* identifier 1..* MS
* identifier ^short = "Patient identifiers (Polaris ID required)"
* identifier ^definition = """
Patient identifiers including:
- **Required**: At least one Polaris system identifier
- **Optional**: Provincial/Territorial Health Numbers (PHN/JHN)
- **Optional**: Raw codes from source EMR systems
- **Optional**: Medical Record Numbers (MRN), other local identifiers

When a source/local 'raw code' identifier exists (e.g. from source EMR),
it SHOULD be included as an additional identifier.

If the raw code's system is known and appropriate (e.g. a recognized code system URI), use that.
If no recognized system exists, implementers MAY:
  • invent a URI for the system, OR
  • use the fallback URI template: https://fhir.apps.health/NamingSystem/<instance-id>-patient-raw-code
"""

// Identifier slicing setup
* identifier ^slicing.discriminator[0].type = #pattern
* identifier ^slicing.discriminator[0].path = "system"
* identifier ^slicing.discriminator[1].type = #pattern  
* identifier ^slicing.discriminator[1].path = "type"
* identifier ^slicing.rules = #open
* identifier ^slicing.description = "Slice identifiers by system and type"

// Define slices
* identifier contains
    polarisId 1..1 MS and
    rawCode 0..* MS and
    jurisdictional 0..* MS

// Polaris ID slice (required)
* identifier[polarisId] ^short = "Polaris Patient ID"
* identifier[polarisId] ^definition = "The primary Polaris identifier for this patient, following the standard Polaris naming pattern"
* identifier[polarisId].system 1..1
* identifier[polarisId].system ^short = "Polaris patient identifier system: https://fhir.apps.health/NamingSystem/<instance-id>-patient-identifier"
* identifier[polarisId].system ^definition = "Must match the pattern: https://fhir.apps.health/NamingSystem/<instance-id>-patient-identifier"
* identifier[polarisId].system ^comment = "Example: https://fhir.apps.health/NamingSystem/cedarbrook-clinic-patient-identifier"
* identifier[polarisId].value 1..1
* identifier[polarisId].value ^short = "The actual Polaris patient ID value: unique in source system"
* identifier[polarisId].use = #official

// Raw code identifier slice (optional, multiple allowed)
* identifier[rawCode] ^short = "Source EMR patient identifier"
* identifier[rawCode] ^definition = "The original raw identifier(s) from the source EMR system"
* identifier[rawCode].system 1..1
* identifier[rawCode].system ^short = "Source system URI or https://fhir.apps.health/NamingSystem/<instance-id>-patient-raw-code"
* identifier[rawCode].system ^definition = """
URI identifying the code system for the raw code.
If the system is known (e.g. EMR-specific patient ID system), use the appropriate URI.
If unknown, use a constructed URI or the fallback:
`https://fhir.apps.health/NamingSystem/<instance-id>-patient-raw-code`
"""
* identifier[rawCode].value 1..1
* identifier[rawCode].use = #secondary

// Jurisdictional identifier slice (optional, multiple allowed for multi-provincial patients)
* identifier[jurisdictional] ^short = "Provincial/Territorial Health Number"
* identifier[jurisdictional] ^definition = "Provincial or Territorial Health Number (PHN/JHN) from Canadian jurisdictions"
* identifier[jurisdictional].type 0..1
* identifier[jurisdictional].type ^short = "Assume #JHN if absent"
* identifier[jurisdictional].type.coding 1..1
* identifier[jurisdictional].type.coding.system = "http://terminology.hl7.org/CodeSystem/v2-0203" (exactly)
* identifier[jurisdictional].type.coding.code = #JHN
* identifier[jurisdictional].type.coding.display = "Jurisdictional health number"
* identifier[jurisdictional].system 1..1
* identifier[jurisdictional].system from $CanadianPHNSystems (required)
* identifier[jurisdictional].system ^short = "PHN system from Canadian PHN systems ValueSet"
* identifier[jurisdictional].system ^definition = "Must be one of the defined Canadian PHN system URIs (e.g., ca-ab-patient-healthcare-id for Alberta)"
* identifier[jurisdictional].value 1..1
* identifier[jurisdictional].value ^short = "The actual PHN value"
* identifier[jurisdictional].use = #official
 
// Narrative element with human-readable summary
* text 0..1 
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the resource for human interpretation."

// Active Status: Whether this patient's record is in active use
* active 0..1 MS
* active ^short = "Whether this patient record is in active use"
* active ^definition = "Indicates whether this patient record is in active use. Many systems use this property to mark as non-active patients who have not been seen for a period of time, died, or left the practice. When absent, the patient record is assumed to be active."

// At least one name, Family/Given required.
* name 1..* MS
* name ^short = "A name associated with the patient"
* name ^definition = "The patient's name(s). At least one name must be provided with either a family or given name. Multiple names can be recorded to capture legal names, preferred names, previous names, and aliases."
* name.family 0..1 MS
* name.family ^short = "Family name (often called 'Surname')"
* name.family ^definition = "The part of a name that links to the genealogy. In some cultures (e.g. Eritrea) the family name of a child is the given name of their father."
* name.given 0..* MS
* name.given ^short = "Given names (not always 'first')"
* name.given ^definition = "Given name(s) including first name, middle names, initials. The first given name is typically used as the primary given name."

// Telecom
* telecom 0..* MS
* telecom ^short = "Contact details for the patient"
* telecom ^definition = "Contact details (telephone, email, etc.) for the patient. May include phone numbers, email addresses, and other electronic communication methods. Systems should capture the use (home, work, mobile) and preference ranking when available."

// Gender: Administrative gender
// if missing, unknown (but know this can cause problems for patient matching)
* gender 0..1 MS
* gender ^short = "Administrative gender (male | female | other | unknown)"
* gender ^definition = "Administrative gender used for identification and administrative purposes. When absent, gender is unknown. Note that this may cause challenges with patient matching algorithms. This is distinct from birth sex and clinical sex parameters which may be captured in extensions."
* gender ^binding.strength = #required
* gender ^binding.valueSet = "http://hl7.org/fhir/ValueSet/administrative-gender"

// Birth Date - if missing, unknown (but know this can cause problems for patient matching)
* birthDate 0..1 MS
* birthDate ^short = "The date of birth for the patient"
* birthDate ^definition = "The date of birth for the patient. When absent, birth date is unknown. Note that missing birth dates may impact patient matching accuracy and age-based clinical decision support. Partial dates (year or year-month only) are permitted when full date is unknown." 

// Deceased Status - if missing, assumed to be alive.
* deceased[x] 0..1 MS
* deceased[x] ^short = "Indicates if the patient is deceased"
* deceased[x] ^definition = "Indicates if the patient is deceased or not. Can be a boolean (true if deceased) or the actual date/time of death. When absent, the patient is assumed to be alive. Systems should update this field promptly when death information is received."

// Address
* address 0..* MS
* address ^short = "Addresses for the patient"
* address ^definition = "Patient addresses including home, temporary, billing addresses. Should follow Canadian address formatting standards including valid province codes and postal codes in A1A 1A1 format. Multiple addresses can be recorded with different uses (home, work, temp, billing)."
* address.postalCode 0..1 MS
* address.postalCode ^definition = "The postal code for the patient's address. Should be in A1A 1A1 format."

// Marital Status
* maritalStatus 0..1 MS
* maritalStatus ^short = "Marital status with raw code support"
* maritalStatus ^definition = """
The patient's most recent marital (civil) status.

RECOMMENDED: Standard terminology codes (v3 Marital Status) when available
OPTIONAL: Raw EMR codes for source system preservation

Used for administrative and billing purposes, may impact insurance coverage and next-of-kin determinations.
"""

// Slicing on the coding array for maritalStatus
* maritalStatus.coding ^slicing.discriminator.type = #pattern
* maritalStatus.coding ^slicing.discriminator.path = "system"
* maritalStatus.coding ^slicing.rules = #open
* maritalStatus.coding ^slicing.description = "Slice by code system"

// Define the slices for maritalStatus
* maritalStatus.coding contains
    standard 0..* MS and
    rawCode 0..* MS

// Standard terminology slice for maritalStatus
* maritalStatus.coding[standard] ^short = "Standard marital status code"
* maritalStatus.coding[standard] ^definition = "Standard terminology code for marital status when available"
* maritalStatus.coding[standard].system 1..1
* maritalStatus.coding[standard].system ^short = "Standard terminology system URI"
* maritalStatus.coding[standard].code 1..1
* maritalStatus.coding[standard].display 0..1

// Raw code slice for maritalStatus
* maritalStatus.coding[rawCode] ^short = "Source EMR marital status code"
* maritalStatus.coding[rawCode] ^definition = "Original marital status code from source EMR system"
* maritalStatus.coding[rawCode].system 1..1
* maritalStatus.coding[rawCode].system ^short = "Source system URI or https://fhir.apps.health/NamingSystem/<instance-id>-patient-maritalstatus-raw-code"
* maritalStatus.coding[rawCode].system ^definition = """
URI for the source EMR's marital status coding system.
If no standard URI exists, use the pattern:
https://fhir.apps.health/NamingSystem/<instance-id>-patient-maritalstatus-raw-code
"""
* maritalStatus.coding[rawCode].code 1..1
* maritalStatus.coding[rawCode].code ^short = "EMR-specific marital status code"
* maritalStatus.coding[rawCode].display 1..1
* maritalStatus.coding[rawCode].display ^short = "EMR's display text for this marital status" 

// Multiple Birth
* multipleBirth[x] 0..1
* multipleBirth[x] ^short = "Whether patient is part of multiple birth"
* multipleBirth[x] ^definition = "Indicates whether the patient is part of a multiple birth (twins, triplets, etc.) and if so, the birth order. Can be boolean (true if multiple birth) or integer (birth order: 1, 2, 3, etc.). Important for pediatric care and family medical history." 

// Communication: for language preference.
* communication 0..* MS
* communication ^short = "Language communication capabilities"
* communication ^definition = "Languages which may be used to communicate with the patient about their health. Important for ensuring effective communication, interpreter services, and meeting Canadian bilingual healthcare requirements."
* communication.language 1..1 MS
* communication.language ^short = "Language with raw code support"
* communication.language ^definition = """
The language for communication with the patient.

RECOMMENDED: ISO 639 language codes (e.g., en, fr) or BCP 47 codes
OPTIONAL: Raw EMR codes for source system preservation

Should include relevant languages such as English (en), French (fr), and indigenous languages.
The preferred language should be marked with communication.preferred = true.
"""

// Slicing on the coding array for language
* communication.language.coding ^slicing.discriminator.type = #pattern
* communication.language.coding ^slicing.discriminator.path = "system"
* communication.language.coding ^slicing.rules = #open
* communication.language.coding ^slicing.description = "Slice by code system"

// Define the slices for language
* communication.language.coding contains
    standard 0..* MS and
    rawCode 0..* MS

// Standard terminology slice for language
* communication.language.coding[standard] ^short = "Standard language code (ISO 639, BCP 47)"
* communication.language.coding[standard] ^definition = "Standard language code when available"
* communication.language.coding[standard].system 1..1
* communication.language.coding[standard].system ^short = "Standard language system URI"
* communication.language.coding[standard].code 1..1
* communication.language.coding[standard].display 0..1

// Raw code slice for language
* communication.language.coding[rawCode] ^short = "Source EMR language code"
* communication.language.coding[rawCode] ^definition = "Original language code from source EMR system"
* communication.language.coding[rawCode].system 1..1
* communication.language.coding[rawCode].system ^short = "Source system URI or https://fhir.apps.health/NamingSystem/<instance-id>-patient-language-raw-code"
* communication.language.coding[rawCode].system ^definition = """
URI for the source EMR's language coding system.
If no standard URI exists, use the pattern:
https://fhir.apps.health/NamingSystem/<instance-id>-patient-language-raw-code
"""
* communication.language.coding[rawCode].code 1..1
* communication.language.coding[rawCode].code ^short = "EMR-specific language code"
* communication.language.coding[rawCode].display 1..1
* communication.language.coding[rawCode].display ^short = "EMR's display text for this language"

// General Practitioner:  Reference PractitionerRole.
* generalPractitioner 0..* MS
* generalPractitioner only Reference(PolarisCorePractitionerRole)
* generalPractitioner ^short = "Patient's primary care provider(s)"
* generalPractitioner ^definition = "Reference to the patient's primary care provider(s), typically their family physician or primary care team. Links to PractitionerRole to capture both the practitioner and their role/organization. Multiple providers can be listed for team-based care models."

// Managing Organization: Reference Organization.
* managingOrganization 1..1
* managingOrganization only Reference(PolarisCoreOrganization)
* managingOrganization ^short = "Organization that maintains the patient record"
* managingOrganization ^definition = "The organization that has primary responsibility for maintaining this patient record in their EMR system. This is typically the primary care clinic or practice where the patient is rostered. Required to establish data ownership and governance."




// Alias for the Canadian PHN Systems ValueSet
Alias: $CanadianPHNSystems = https://fhir.apps.health/ValueSet/canadian-phn-systems

// CodeSystem for Canadian Provincial Health Number Systems URIs
// Defines the standard naming systems for provincial and territorial health numbers across Canada

CodeSystem: CanadianPHNSystemsCS
Id: cs-canadian-phn-systems
Title: "Canadian Provincial Health Number Systems CodeSystem"
Description: "URIs for provincial and territorial health number naming systems across Canada"
* ^status = #active
* ^experimental = false
* ^caseSensitive = true
* ^content = #complete
* ^url = "https://fhir.apps.health/CodeSystem/canadian-phn-systems"
* #ca-ab-patient-healthcare-id "Alberta Health Care Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-ab-patient-healthcare-id"
* #ca-bc-patient-healthcare-id "British Columbia PHN" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-bc-patient-healthcare-id"
* #ca-mb-patient-healthcare-id "Manitoba Health Registration Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-mb-patient-healthcare-id"
* #ca-nb-patient-healthcare-id "New Brunswick Medicare Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-nb-patient-healthcare-id"
* #ca-nl-patient-healthcare-id "Newfoundland and Labrador MCP Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-nl-patient-healthcare-id"
* #ca-ns-patient-healthcare-id "Nova Scotia Health Card Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-ns-patient-healthcare-id"
* #ca-nt-patient-healthcare-id "Northwest Territories Health Care Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-nt-patient-healthcare-id"
* #ca-nu-patient-healthcare-id "Nunavut Health Care Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-nu-patient-healthcare-id"
* #ca-on-patient-hcn "Ontario Health Card Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-patient-hcn"
* #ca-pe-patient-healthcare-id "Prince Edward Island Health Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-pe-patient-healthcare-id"
* #ca-qc-patient-healthcare-id "Quebec Health Insurance Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-qc-patient-healthcare-id"
* #ca-sk-patient-healthcare-id "Saskatchewan Health Services Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-sk-patient-healthcare-id"
* #ca-yt-patient-healthcare-id "Yukon Health Care Number" "https://fhir.infoway-inforoute.ca/NamingSystem/ca-yt-patient-healthcare-id"

ValueSet: CanadianPHNSystems
Id: vs-canadian-phn-systems
Title: "Canadian Provincial Health Number Systems"
Description: "Naming systems for provincial and territorial health numbers across Canada, based on Infoway standards"
* ^status = #active
* ^experimental = false
* ^jurisdiction = urn:iso:std:iso:3166#CA "Canada"
* ^version = "1.0.0"
* ^url = "https://fhir.apps.health/ValueSet/canadian-phn-systems"
* include codes from system https://fhir.apps.health/CodeSystem/canadian-phn-systems