<commit_before># == Schema Information
#
# Table name: orders
#
#  id                    :integer          not null, primary key
#  order_total           :decimal(, )      default(0.0), not null
#  number_of_items       :string           default("1"), not null
#  special_instructions  :string
#  shipping_reference    :string
#  estimated_weight      :string           default("1"), not null
#  signature_requirement :integer          default("No Signature"), not null
#  pickup_date           :date
#  pickup_time           :string
#  service_id            :integer
#  account_id            :integer
#  created_at            :datetime         not null
#  updated_at            :datetime         not null
#

class Order < ApplicationRecord

  # TODO: Add to services table
  BIZ_START_TIME  = "08:00"
  BIZ_END_TIME = "16:00"
  TIME_REGEX = /\A(2[0-3]|[01]?[0-9]):([0-5]?[0-9])( )?([APap][mM])?\z/

  attr_accessor :parsed_pickup_time

  enum signature_requirement: { "No Signature" => 0, "Direct Signature" => 1, "Indirect Signature" => 2 }

  before_validation :set_pickup_time, if: :pickup_time_valid?

  has_one :shipper, inverse_of: :order
  has_one :recipient, inverse_of: :order
  belongs_to :service, inverse_of: :orders
  belongs_to :account, inverse_of: :orders

  validates_associated :shipper

  validates_associated :recipient

  validates :pickup_date, presence: true

  validates :pickup_time, presence: true

  validates :service, presence: true

  validates :special_instructions, length: { maximum: 2000 },
                                   allow_blank: true

  validates :signature_requirement, presence: true,
                                    inclusion: { within: ["No Signature", "Indirect Signature", "Direct Signature"] }

  validates :estimated_weight, presence: true
  validates :estimated_weight, numericality: { greater_than_or_equal_to: 1 },
                               if: "estimated_weight.present?"

  validates :number_of_items, presence: true
  validates :number_of_items, numericality: { greater_than_or_equal_to: 1 },
                              if: "number_of_items.present?"

  validates :shipping_reference, length: { maximum: 50 },
                                 allow_blank: true

  # TODO: Add later
  # validates :order_total, presence: true, numericality: { greater_than_or_equal_to: 0 }

  # TODO: Add scope for searching orders w/autocomplete
  default_scope { order(id: :desc) }

  def shipper_attributes=(shipper_attributes)
    shipper = self.build_shipper(
      first_name: shipper_attributes[:first_name],
      last_name:  shipper_attributes[:last_name]
      )

    shipper.build_phone(
      phone_number: shipper_attributes[:phone][:phone_number],
        phone_type: shipper_attributes[:phone][:phone_type],
               ext: shipper_attributes[:phone][:ext]
      )

    shipper.build_address(
      secondary_address: shipper_attributes[:address][:secondary_address],
         street_address: shipper_attributes[:address][:street_address],
           address_type: shipper_attributes[:address][:address_type],
                    zip: shipper_attributes[:address][:zip]
      )
  end

  def recipient_attributes=(recipient_attributes)
    recipient = self.build_recipient(
      first_name: recipient_attributes[:first_name],
      last_name:  recipient_attributes[:last_name]
      )

    recipient.build_phone(
      phone_type:   recipient_attributes[:phone][:phone_type],
      phone_number: recipient_attributes[:phone][:phone_number],
      ext:          recipient_attributes[:phone][:ext]
      )

    recipient.build_address(
      address_type:      recipient_attributes[:address][:address_type],
      street_address:    recipient_attributes[:address][:street_address],
      secondary_address: recipient_attributes[:address][:secondary_address],
      zip:               recipient_attributes[:address][:zip]
      )
  end

  # NOTE: Custom validations
  validate :pickup_time_format_is_valid, unless: "pickup_time.blank?"
  validate :pickup_date_not_in_the_past,
           :pickup_time_not_in_the_past,
           :pickup_within_biz_hours,
           :pickup_within_serv_hours,
              if: :pickup_date_and_time_are_present?

  private

    def set_pickup_time
      self.pickup_time = Time.zone.parse(parsed_pickup_time).strftime('%H:%M')
    end

    def pickup_time_valid?
      self.parsed_pickup_time = pickup_time.gsub(/\s+/, '').downcase
      @pickup_time_valid = parsed_pickup_time.match(TIME_REGEX).present? &&
                           pickup_time.present?<commit_msg>Add .search method to query orders<commit_after># == Schema Information
#
# Table name: orders
#
#  id                    :integer          not null, primary key
#  order_total           :decimal(, )      default(0.0), not null
#  number_of_items       :string           default("1"), not null
#  special_instructions  :string
#  shipping_reference    :string
#  estimated_weight      :string           default("1"), not null
#  signature_requirement :integer          default("No Signature"), not null
#  pickup_date           :date
#  pickup_time           :string
#  service_id            :integer
#  account_id            :integer
#  created_at            :datetime         not null
#  updated_at            :datetime         not null
#

class Order < ApplicationRecord

  # TODO: Add to services table
  BIZ_START_TIME  = "08:00"
  BIZ_END_TIME = "16:00"
  TIME_REGEX = /\A(2[0-3]|[01]?[0-9]):([0-5]?[0-9])( )?([APap][mM])?\z/

  attr_accessor :parsed_pickup_time

  enum signature_requirement: { "No Signature" => 0, "Direct Signature" => 1, "Indirect Signature" => 2 }

  before_validation :set_pickup_time, if: :pickup_time_valid?

  has_one :shipper, inverse_of: :order
  has_one :recipient, inverse_of: :order
  belongs_to :service, inverse_of: :orders
  belongs_to :account, inverse_of: :orders

  validates_associated :shipper

  validates_associated :recipient

  validates :pickup_date, presence: true

  validates :pickup_time, presence: true

  validates :service, presence: true

  validates :special_instructions, length: { maximum: 2000 },
                                   allow_blank: true

  validates :signature_requirement, presence: true,
                                    inclusion: { within: ["No Signature", "Indirect Signature", "Direct Signature"] }

  validates :estimated_weight, presence: true
  validates :estimated_weight, numericality: { greater_than_or_equal_to: 1 },
                               if: "estimated_weight.present?"

  validates :number_of_items, presence: true
  validates :number_of_items, numericality: { greater_than_or_equal_to: 1 },
                              if: "number_of_items.present?"

  validates :shipping_reference, length: { maximum: 50 },
                                 allow_blank: true

  # TODO: Add later
  # validates :order_total, presence: true, numericality: { greater_than_or_equal_to: 0 }

  # TODO: Add scope for searching orders w/autocomplete
  default_scope { order(id: :desc) }

  def shipper_attributes=(shipper_attributes)
    shipper = self.build_shipper(
      first_name: shipper_attributes[:first_name],
      last_name:  shipper_attributes[:last_name]
      )

    shipper.build_phone(
      phone_number: shipper_attributes[:phone][:phone_number],
        phone_type: shipper_attributes[:phone][:phone_type],
               ext: shipper_attributes[:phone][:ext]
      )

    shipper.build_address(
      secondary_address: shipper_attributes[:address][:secondary_address],
         street_address: shipper_attributes[:address][:street_address],
           address_type: shipper_attributes[:address][:address_type],
                    zip: shipper_attributes[:address][:zip]
      )
  end

  def recipient_attributes=(recipient_attributes)
    recipient = self.build_recipient(
      first_name: recipient_attributes[:first_name],
      last_name:  recipient_attributes[:last_name]
      )

    recipient.build_phone(
      phone_type:   recipient_attributes[:phone][:phone_type],
      phone_number: recipient_attributes[:phone][:phone_number],
      ext:          recipient_attributes[:phone][:ext]
      )

    recipient.build_address(
      address_type:      recipient_attributes[:address][:address_type],
      street_address:    recipient_attributes[:address][:street_address],
      secondary_address: recipient_attributes[:address][:secondary_address],
      zip:               recipient_attributes[:address][:zip]
      )
  end

  # NOTE: Custom validations
  validate :pickup_time_format_is_valid, unless: "pickup_time.blank?"
  validate :pickup_date_not_in_the_past,
           :pickup_time_not_in_the_past,
           :pickup_within_biz_hours,
           :pickup_within_serv_hours,
              if: :pickup_date_and_time_are_present?

  def self.search(search)
    if search
      where("id = ?", search.try(:squish)).take!
    else
      self.all
    end
  end

  private

    def set_pickup_time
      self.pickup_time = Time.zone.parse(parsed_pickup_time).strftime('%H:%M')
    end

    def pickup_time_valid?
      self.parsed_pickup_time = pickup_time.gsub(/\s+/, '').downcase
      @pickup_time_valid = parsed_pickup_time.match(TIME_REGEX).present? &&
                           pickup_time.present?
{'fraction_non_alphanumeric': 0.09947249434815374, 'fraction_numerical': 0.007535795026375283, 'mean_word_length': 2.993981083404987, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2927567', 'n_tokens_mistral': 2903, 'n_tokens_neox': 2780, 'n_words': 540}