class R4FhirQuestionnaireResponseToProgramStageTransformer extends AbstractFhirQuestionnaireResponseToProgramStageTransformer {

    @Override
    protected void transformInternal( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo<ProgramStageRule> ruleInfo, @Nonnull Map<String, Object> scriptVariables, @Nonnull FhirResourceMapping fhirResourceMapping, @Nonnull Program program,
        @Nonnull ProgramStage programStage, @Nonnull IBaseResource questionnaireResponse, @Nonnull ScriptedTrackedEntityInstance scriptedTrackedEntityInstance, @Nonnull WritableScriptedEvent scriptedEvent ) throws TransformerException
    {
        final QuestionnaireResponse fhirQuestionnaireResponse = (QuestionnaireResponse) questionnaireResponse;
        final Event event = (Event) scriptedEvent.getDhisResource();
        final String enrollmentId = getAssignedDhisId( context, ruleInfo, scriptVariables, fhirQuestionnaireResponse.getBasedOnFirstRep().getReferenceElement() );

        if ( enrollmentId == null )
        {
            throw new TransformerDataException( "Enrollment could not be found: " +
                fhirQuestionnaireResponse.getPartOfFirstRep().getReferenceElement().getValue() );
        }

        event.setEnrollmentId( enrollmentId );
        event.setProgramId( program.getId() );
        event.setProgramStageId( programStage.getId() );
        event.setStatus( convertStatus( fhirQuestionnaireResponse ) );
        event.setOrgUnitId( getOrgUnitId( context, ruleInfo, fhirResourceMapping.getImpEnrollmentOrgLookupScript(), scriptVariables )
            .orElseThrow( () -> new TransformerMappingException( "Care plan contains location that cannot be mapped." ) ) );
        event.setTrackedEntityInstanceId( scriptedTrackedEntityInstance.getId() );
        event.setStatus( convertStatus( fhirQuestionnaireResponse ) );

        final Date eventDate = fhirQuestionnaireResponse.hasAuthored() ? fhirQuestionnaireResponse.getAuthored() : null;
        event.setEventDate( eventDate == null ? null : ZonedDateTime.ofInstant( eventDate.toInstant(), zoneId ) );
        final Date dueDate = DueDateExtensionUtils.getValue( fhirQuestionnaireResponse );
        event.setDueDate( dueDate == null ? null : ZonedDateTime.ofInstant( dueDate.toInstant(), zoneId ) );

        final AbstractValueTypeFhirToDhisTransformerUtils valueTypeUtils =
            TransformerUtils.getScriptVariable( scriptVariables, ScriptVariable.VALUE_TYPE_UTILS, AbstractValueTypeFhirToDhisTransformerUtils.class );
        final Set<String> dataElementIds = new HashSet<>();

        fhirQuestionnaireResponse.getItem().forEach( item -> {
            final ProgramStageDataElement dataElement = programStage.getOptionalDataElement( Reference.createIdReference( item.getLinkId() ) )
                .orElseThrow( () -> new TransformerDataException( "Data element ID " + item.getLinkId() + " has not been defined for program stage ID " + programStage.getId() ) );

            final WritableDataValue dataValue = event.getDataValue( dataElement.getElementId() );
            dataValue.setValue( valueTypeUtils.convert( item.getAnswerFirstRep().getValue(), dataElement.getElement().getValueType() ) );
            dataValue.setModified();

            dataElementIds.add( item.getLinkId() );
        } );

        event.getDataValues().removeIf( dataValue -> !dataElementIds.contains( dataValue.getDataElementId() ) );
        event.setModified();
    }

    public  R4FhirQuestionnaireResponseToProgramStageTransformer( @Nonnull ScriptExecutor scriptExecutor, @Nonnull TrackedEntityMetadataService trackedEntityMetadataService, @Nonnull TrackedEntityService trackedEntityService,
        @Nonnull TrackedEntityRuleRepository trackedEntityRuleRepository, @Nonnull OrganizationUnitService organizationUnitService, @Nonnull ProgramMetadataService programMetadataService, @Nonnull ProgramStageMetadataService programStageMetadataService,
        @Nonnull EventService eventService, @Nonnull FhirResourceMappingRepository resourceMappingRepository, @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, @Nonnull ScriptExecutionContext scriptExecutionContext,
        @Nonnull ValueConverter valueConverter );

}