# Copyright (C) 2024 Manuel Bustillo

# frozen_string_literal: true

require_relative '../../extensions/tree_node_extension'

module Tables
  class Distribution
    attr_accessor :tables, :min_per_table, :max_per_table

    def initialize(min_per_table:, max_per_table:)
      @min_per_table = min_per_table
      @max_per_table = max_per_table
      @tables = []
    end

    def random_distribution(people)
      min_tables = (people.count * 1.0 / @max_per_table).ceil
      max_tables = (people.count * 1.0 / @min_per_table).ceil
      @tables = people.in_groups(rand(min_tables..max_tables), false)
                      .map { |group| Table.new(group) }
                      .each { |table| table.min_per_table = @min_per_table }
                      .each { |table| table.max_per_table = @max_per_table }
    end

    def discomfort
      @tables.map do |table|
        local_discomfort(table)
      end.sum
    end

    def inspect
      "#{@tables.count} tables, discomfort: #{discomfort}"
    end

    def pretty_print
      @tables.map.with_index do |table, i|
        "Table #{i + 1} (#{table.count} ppl): (#{local_discomfort(table)}) #{table.map(&:name).join(', ')}"
      end.join("\n")
    end

    def deep_dup
      self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution|
        new_distribution.tables = @tables.map(&:dup)
      end
    end

    def save!
      ActiveRecord::Base.transaction do
        arrangement = TablesArrangement.create!

        records_to_store = []

        tables.each_with_index do |table, table_number|
          table.each do |person|
            records_to_store << { guest_id: person.id, tables_arrangement_id: arrangement.id, table_number: }
          end
        end

        Seat.insert_all!(records_to_store)

        arrangement.update!(discomfort:)
      end
    end

    private

    def local_discomfort(table)
      table.discomfort ||= DiscomfortCalculator.new(table:).calculate
    end
  end
end