# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024-2025 LibreWeddingPlanner contributors # frozen_string_literal: true class AffinityGroupsHierarchy < Array DEFAULT_DISCOMFORT = 1 def initialize super @references = {} Group.roots.each do |group| self << group.id hydrate(group) end load_discomforts freeze end def find(id) @references[id] end def <<(id) new_node = Tree::TreeNode.new(id) super(new_node).tap { @references[id] = new_node } end def register_child(parent_id, child_id) @references[parent_id] << Tree::TreeNode.new(child_id).tap { |child_node| @references[child_id] = child_node } end def distance(id_a, id_b) return nil if @references[id_a].nil? || @references[id_b].nil? @references[id_a].distance_to_common_ancestor(@references[id_b]) end def discomfort(id_a, id_b) return 0 if id_a == id_b @discomforts[uuid_to_int(id_a) + uuid_to_int(id_b)] || DEFAULT_DISCOMFORT end def default_discomfort(id_a, id_b) return 0 if id_a == id_b dist = distance(id_a, id_b) return DEFAULT_DISCOMFORT if dist.nil? Rational(dist, dist + 1) end private def load_discomforts @load_discomforts ||= GroupAffinity.pluck(:group_a_id, :group_b_id, :discomfort).each_with_object({}) do |(id_a, id_b, discomfort), acc| acc[uuid_to_int(id_a) + uuid_to_int(id_b)] = discomfort end end def uuid_to_int(uuid) uuid.gsub('-', '').hex end def hydrate(group) group.children.each do |child| register_child(group.id, child.id) hydrate(child) end end end