# frozen_string_literal: true

require_dependency "renalware/pathology"
require_dependency "attr_extras"

module Renalware
  module Pathology
    class ObservationSetPresenter < DumbDelegator
      class Observation
        attr_reader_initialize [:code, :description, :result, :observed_at]
      end

      def method_missing(method_name, **_args, &_block)
        return if __getobj__.nil?

        vals = __getobj__.values
        vals.public_send(method_name)
      end

      def respond_to_missing?(method_name, _include_private = false)
        (values.present? && values.respond_to?(method_name)) || super
      end

      def each_display_group
        return unless block_given?

        ObservationDescription
          .in_display_order
          .to_a
          .group_by(&:display_group)
          .each do |group_number, array_of_obs_desc|

          group = array_of_obs_desc.map do |obs_desc|
            observation_hash = send(obs_desc.code.to_sym) || {}
            observed_at = observation_hash["observed_at"]
            Observation.new(
              code: obs_desc.code,
              result: observation_hash["result"],
              observed_at: observed_at && ::Time.zone.parse(observed_at),
              description: obs_desc
            )
          end

          yield group, group_number
        end
      end

      private

      def build_observation(code:, observation_hash:, with_description: false)
        Observation.new(
          code: code,
          result: observation_hash["result"],
          observed_at: ::Time.zone.parse(observation_hash["observed_at"]),
          description: with_description ? description_for(code) : nil
        )
      end

      def description_for(code)
        observation_description_map.fetch(code, "#{code} (no description found!)")
      end

      def observation_description_map
        @observation_description_map ||= begin
          ::Renalware::Pathology::ObservationDescription
            .pluck(:code, :name)
            .each_with_object({}) { |desc, hash| hash[desc.first] = desc.last }
        end
      end
    end
  end
end