# frozen_string_literal: true

module Upgrow
  # Mixin that implements Repository methods with an Active Record Base. When
  # included in a Repository class, it sets the default base to be a class
  # ending with `Record`.
  module ActiveRecordQueries
    # Class methods for classes that include this module.
    module ClassMethods
      # Callback method used by Basic Repository to set a default Repository
      # base when one is not explicitly provided at the Repository
      # initialization.
      #
      # It attempts to find a constant based on the Repository name, with the
      # `Record` suffix as a convention. For example, a `UserRepository` would
      # have the `UserRecord` as its base. That is the naming convention for
      # Active Record classes under this architecture.
      #
      # @return [Class] the Active Record Base class to be used as the
      #   Repository base according to the architecture's naming convention.
      def default_base
        base_name = "#{name[/\A(.+)Repository\z/, 1]}Record"
        Object.const_get(base_name)
      end
    end

    # @private
    def self.included(base)
      base.extend(ClassMethods)
    end

    # Fetches all Records and returns them as an Array of Models.
    #
    # @return [Array<Model>] a collection of Models representing all persisted
    #   Records.
    def all
      to_model(base.all)
    end

    # Persists a new Record with the given input, and materializes the newly
    # created Record as the returned Model instance.
    #
    # @param input [Input] the Input with the attributes for the new Record.
    #
    # @return [Model] the Model with the attributes of the newly created
    #   Record.
    def create(input)
      record = base.create!(input.attributes)
      to_model(record)
    end

    # Retrieves the Record with the given ID, representing its data as a Model.
    #
    # @param id [Integer] the ID of the Record to be fetched.
    #
    # @return [Model] the Model with the attributes of the Record with the given
    #   ID.
    def find(id)
      record = base.find(id)
      to_model(record)
    end

    # Updates the Record with the given ID with the given Input attributes.
    #
    # @param id [Integer] the ID of the Record to be updated.
    # @param input [Input] the Input with the attributes to be set in the
    #   Record.
    #
    # @return [Model] the Model instance with the updated data of the Record.
    def update(id, input)
      record = base.update(id, input.attributes)
      to_model(record)
    end

    # Deletes the Record that has the given ID.
    #
    # @param id [Integer] the ID of the Record to be deleted.
    def delete(id)
      base.destroy(id)
    end
  end
end