require_dependency "renalware/system"
require "liquid" # See https://github.com/Shopify/liquid

module Renalware
  module System
    class RenderLiquidTemplate

      def self.call(**args)
        new.call(**args)
      end

      # Takes the #body of a database-stored Template model, parses it with the Liquid gem to insert
      # variables, and return the resulting html.
      # Raises an exception id the template is not found or a variable the template requires was
      # not supplied.
      #
      # The body stored in the template#body could be any an html fragment or the whole page, but if
      # the latter if should be the inner html of the <body> tag and not contain a <body> tag itself
      # Any <style> tags (ie css) will have to be also inside the body, which works fine; there is
      # currently no support for rendering items the page <head> for instance. Any images must be
      # embedded as binary data.
      #
      # This approach allows us to render hospital-defined content in a small number of places -
      # for Barts can print an ESI as a PDF form (eg at patients/xxx/pd/peritonitis_eposides/1.pdf)
      # to let them capture extra detail manually. Using this class lets us search for and load a
      # hospital-defined template to use in an instance as this. It reduces support overhead as
      # the templates table can be updated manually (by someone qualified); for example a designer
      # could make changes to the content and his work (html content with css in a style section as
      # described above) updated in the database.
      #
      # The one caveat is the insertion of variables. These have to be passed in the `variables`
      # argument which is a hash of Liquid::Drop instances (see for example PatientDrop) - this is
      # just a safe read-only decorator around the data you want to use in a template (for
      # example "<h1>{{ patient.name }}</h1>")
      # See https://github.com/Shopify/liquid/wiki/Introduction-to-Drops
      #
      # Note this template approach is for adhoc hospital-defined content only as a means to add
      # customisation in certain edge cases, and should not be leaned on in preference to the
      # conventional rendering of actions/views explicitly, otherwise mayhem will result ;0)
      #
      # Example usage:
      #
      #   RenderLiquidTemplate.call("name_of_my_template",
      #                             patient: PatientDrop.new(patient),
      #                             prescriptions: PrescriptionsDrop.new(patient)
      #
      def call(template_name:, variables: nil)
        liquify_template(template_name, variables)
      end

      private

      def liquify_template(template_name, variables = {})
        Liquid::Template.error_mode = :strict
        template = Template.find_by!(name: template_name)
        liquified_template = Liquid::Template.parse(template.body)
        liquified_template.render!(variables, { strict_variables: true })
      end
    end
  end
end