class Order < ApplicationRecord
  self.primary_key = "order_id"

  #primary key - need to be unique
  before_create :generate_custom_id
  before_create :set_order_date
  has_many :sale_orders
  belongs_to :customer
  has_many :order_items, autosave: true , inverse_of: :order
  has_many :assigned_order_items
  has_one :booking_order
  has_one :booking, through: :booking_order

  #internal references attributes for business logic control
  attr_accessor :items, :guest, :table_id, :new_booking, :booking_type, :employee_name, :booking_id, :extra_time, :is_extra_time
  scope :active, -> { where("date BETWEEN '#{Time.current.utc.end_of_day}' AND '#{Time.current.utc.beginning_of_day}'") }
  #Main Controller method to create new order - validate all inputs and generate new order
  # order_item : {
  #     order_item_code : "",
  #     item_instance_code : "",
  #     quantity : 0,
  #     option_values : [],
  #     sub_order_items : [],
  # }

  def generate
    booking = nil
    if self.new_booking
      #add extra time
      if self.is_extra_time && self.extra_time
        booking = Booking.create({:dining_facility_id => table_id,:type => "TableBooking",
                                :checkin_at => Time.current, :checkout_at => Time.current + self.extra_time.to_i,
                                :checkin_by =>  self.employee_name,
                                :booking_status => "assign" })
      else
        booking = Booking.create({:dining_facility_id => table_id,:type => "TableBooking",
                                :checkin_at => Time.current, :checkin_by =>  self.employee_name,
                                :booking_status => "assign" })
      end
      #end extra time

      if self.table_id.to_i > 0
        table =  DiningFacility.find(self.table_id)
        table.update_attributes(status:"occupied")
        # table.status = "occupied"
        # table.save
      end
    else
      booking = Booking.find(self.booking_id)
      #add extra time
      if self.is_extra_time
        if !booking.checkout_at.nil?
          new_time = booking.checkout_at + self.extra_time.to_i
          booking.update(checkout_at: new_time)
        end
      end
      #end extra time
    end

    self.default_values

    #  cashier already opened?
    if self.save!

      self.adding_line_items
      #Add Order Table and Room relation afrer order creation
      booking.orders << self

      #Send order to queue one it done!
      # if self.source != "quick_service"
      #   process_order_queue
      # end

      #send order to broadcast job
      # send_order_broadcast(booking)

      return true, booking
    end
    return false
  end

  def custom_generate
    booking = nil

    if self.new_booking
      booking = Booking.create({:dining_facility_id => self.table_id,:type => "TableBooking",
                                :checkin_at => Time.current, :checkin_by =>  self.employee_name,
                                :booking_status => "assign" })
      table =  DiningFacility.find(self.table_id)
      table.status = "occupied"
      table.save
    else
      booking = Booking.find(self.booking_id)
    end

    booking.save!
    self.default_values

    #  cashier already opened?
    if self.save!

      self.adding_line_items
      #Add Order Table and Room relation afrer order creation
      booking.orders << self

      #Send order to queue one it done!
      # if self.source != "quick_service"
      #   process_order_queue
      # end

      #send order to broadcast job
      send_order_broadcast(booking)

      return true, booking
    end

    return false

  end

  #Main Method - to update order / add items
  def modify

  end

  def adding_line_items
    if self.items
      #re-order to
      items = re_order_items(self.items)
      #loop to add all items to order
      item_instance_codes = items.map { |i| i[:item_instance_code] }
      menu_items = MenuItem.search_by_item_code(item_instance_codes)
      menu_items += Product.search_by_product_code(item_instance_codes) if menu_items.length < item_instance_codes.length

      items.each do |order_item|
        menu_item = menu_items.find { |i| i[:item_instance_code].downcase == order_item[:item_instance_code].downcase }
        sub_order_items = []
        if order_item[:sub_items].length > 0
          sub_menu_items = MenuItem.search_by_item_code(order_item[:sub_items].map { |i| i[:item_instance_code] })
          order_item[:sub_items].each do |sub_order_item|
            sub_menu_item = sub_menu_items.find { |i| i[:item_instance_code] == sub_order_item[:item_instance_code] }
            sub_order_items << { item_instance_code: sub_order_item[:item_instance_code], item_instance_name: sub_menu_item[:name], quantity: sub_order_item[:quantity], price: sub_menu_item[:price], options: sub_order_item[:options] }
          end
        end
        sub_order_items = sub_order_items.to_json
        self.order_items.build({
          item_code: menu_item[:item_code],
          item_instance_code: order_item[:item_instance_code],
          item_name: menu_item[:name],
          alt_name: menu_item[:alt_name],
          account_id: menu_item[:account_id],
          qty: order_item[:quantity],
          price: menu_item[:price],
          options: order_item[:options],
          set_menu_items: sub_order_items,
          item_order_by: employee_name,
          taxable: menu_item[:taxable]
        })
      end

      self.item_count = self.order_items.count
      self.save!

      return true
    else
      self.errors.add(:order_items, :blank, message: "Order items cannot be blank")
      return false
    end
  end

  def update_items_status_to_billed(items)
    if (items.nil?)
      OrderItem.where({ order_items_id: '#{self.order_items.pluck(:order_items_id)}'}).update_all(order_item_status: "billed")
      ##Update the order status to ensure that reflect the stage
      # self.order_items.each do |item|
      #   item.order_item_status = "billed"
      #   item.save
      # end
    else
      OrderItem.where({ order_items_id: '#{items.pluck(:order_items_id)}'}).update_all(order_item_status: "billed")
      # items.each do |item|
      #   item.order_item_status = "billed"
      #   item.save
      # end
    end
  end

  def default_values
      self.customer = Customer.walkin if self.customer_id.nil?
      self.source = "emenu" if self.source.nil?
      self.order_type = "dine-in" if self.order_type.nil?
  end

  protected
  def re_order_items(items) #reorder inputs items as parents and child
    new_items = items.select { |i| i[:parent_order_item_id].blank? }.map { |x| x[:sub_items] = items.select { |y| x[:order_item_id] == y[:parent_order_item_id] }; x }
  end


  #Update Items Count and Quantity changes whenever there is changes
  def update_products_and_quantity_count
    item_count = 0
    quantity_count = 0
    # Count number of different items
    self.item_count = self.order_items.item_count
    self.quantity_count = quantity_count
    # Counter number of quantityf
  end


  #Process order items and send to order queue
  def self.pay_process_order_queue(id,table_id)
    # if ENV["SERVER_MODE"] != 'cloud'
      order        = Order.find(id)
      sidekiq      = Lookup.find_by_lookup_type("sidekiq")
      if !sidekiq.nil?
        OrderQueueProcessorJob.perform_later(id, table_id)
      else
        if order
          oqs = OrderQueueStation.new
          oqs.process_order(order, table_id)
        end
        assign_order = AssignedOrderItem.assigned_order_item_by_job(id)
        ActionCable.server.broadcast "order_queue_station_channel",order: assign_order
      end
    # end
  end

  #send order items and send to order queue
  def send_order_broadcast(booking)
    if self.table_id.to_i > 0
      table = DiningFacility.find(booking.dining_facility_id)
      type  = 'order'
      #Send to background job for processing
      # OrderBroadcastJob.perform_later(table,type)
      if ENV["SERVER_MODE"] == 'cloud'
        from = request.subdomain  + "." + request.domain
      else
        from = ""
      end
      ActionCable.server.broadcast "order_channel",table: table,type:type,from:from

    end
  end

  #Origami: Cashier : to view order Table
  def self.get_order_table
    order_table = Order.select("orders.order_id as order_id,sum(order_items.qty*order_items.price) as total_price,
        order_items.order_items_id as order_items_id,dining_facilities.name as table_name")
        .joins("left join booking_orders on booking_orders.order_id = orders.order_id
        left join bookings on bookings.booking_id = booking_orders.booking_order_id
        left join dining_facilities on dining_facilities.id = bookings.dining_facility_id
        left join order_items on order_items.order_id = orders.order_id")
        .where("dining_facilities.type=? and orders.order_type=? and dining_facilities.is_active=?",DiningFacility::TABLE_TYPE,"dine_in",true)
        .group("orders.order_id, order_items.order_items_id,dining_facilities.name")
  end

  #Origami: Cashier : to view booking order Table
  def self.get_booking_order_table
    booking_orders = Booking.select("sales.receipt_no,orders.status as order_status, sales.sale_status as sale_status,
                        orders.order_id as order_id,sales.customer_id as sale_customer_id,orders.customer_id as order_customer_id,
                        bookings.booking_id,sales.sale_id as sale_id,dining_facilities.name as table_name")
                    .joins("left join booking_orders on booking_orders.booking_id = bookings.booking_id")
                    .joins("left join dining_facilities on dining_facilities.id = bookings.dining_facility_id")
                    .joins("left join orders on orders.order_id = booking_orders.order_id")
                    .joins("left join sales on sales.sale_id = bookings.sale_id")
                    .where("(orders.status = 'new' or orders.status = 'billed') and (dining_facilities.type=? and dining_facilities.is_active=?)",DiningFacility::TABLE_TYPE,true)
                    .group("bookings.booking_id,sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.status,orders.order_id")
                    # For PG
                    # booking_orders.order_id IS NOT NULL and dining_facilities.type=? and dining_facilities.is_active=?",DiningFacility::TABLE_TYPE,true
                    # sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.status,orders.order_id
  end

  #Origami: Cashier : to view booking order Table
  def self.get_completed_order
    completed_orders = Booking.select("sales.receipt_no, sales.sale_status, orders.status as order_status,orders.order_id,
                        bookings.booking_id,sales.sale_id as sale_id,dining_facilities.name as table_name,sales.customer_id as sale_customer_id,orders.customer_id as order_customer_id")
                    .joins("left join booking_orders on booking_orders.booking_id = bookings.booking_id")
                    .joins("left join dining_facilities on dining_facilities.id = bookings.dining_facility_id")
                    .joins("left join orders on orders.order_id = booking_orders.order_id")
                    .joins("left join sales on sales.sale_id = bookings.sale_id")
                    .where("sales.sale_status='completed'")
                    .group("sales.sale_id,bookings.booking_id,sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.status,orders.order_id")
                    # For PG
                    #bookings.booking_id,sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.status,orders.order_id
  end

   #Origami: Cashier : to view order type Room
  def self.get_booking_order_rooms
    booking_rooms = Booking.select("sales.receipt_no,orders.status as order_status, sales.sale_status as sale_status,
                        orders.order_id as order_id,sales.customer_id as sale_customer_id,orders.customer_id as order_customer_id,
                        bookings.booking_id,orders.customer_id as customer_id,
                        sales.sale_id as sale_id,dining_facilities.name as room_name")
                    .joins("left join booking_orders on booking_orders.booking_id = bookings.booking_id")
                    .joins("left join dining_facilities on dining_facilities.id = bookings.dining_facility_id")
                    .joins("left join orders on orders.order_id = booking_orders.order_id")
                    .joins("left join sales on sales.sale_id = bookings.sale_id")
                    .where("(orders.status = 'new' or orders.status = 'billed') and (dining_facilities.type=? and dining_facilities.is_active=?)",DiningFacility::ROOM_TYPE,true)
                    .group("bookings.booking_id,sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.customer_id,orders.order_id")
                    # For PG
                    # booking_orders.order_id IS NOT NULL and dining_facilities.type=? and dining_facilities.is_active=?",DiningFacility::ROOM_TYPE,true
                    # sales.receipt_no,orders.status,sales.sale_id,dining_facilities.name,orders.customer_id,orders.order_id
  end

   #Origami: Cashier : to view order type Room
  def self.get_order_rooms
    order_rooms = Order.select("orders.order_id as order_id,sum(order_items.qty*order_items.price) as total_price,
                    order_items.id as order_items_id,dining_facilities.name as room_name")
          .joins("left join booking_orders on booking_orders.order_id = orders.order_id
          left join bookings on bookings.booking_id = booking_orders.order_id
          left join dining_facilities on dining_facilities.id = bookings.dining_facility_id
          left join order_items on order_items.order_id = orders.order_id")
          .where("dining_facilities.type=? and orders.order_type=? and dining_facilities.is_active=?",DiningFacility::ROOM_TYPE,"dine_in",true)
          .group("orders.order_id")
  end

  #Origami: Cashier : to view orders
  def self.get_orders
    from = Time.current.beginning_of_day.utc
    to = Time.current.end_of_day.utc
    orders=Booking.select("sales.receipt_no, sales.sale_status as sale_status, orders.status as order_status,
                        orders.order_id as order_id,sales.customer_id as sale_customer_id,orders.customer_id as order_customer_id,
                        bookings.booking_id,orders.customer_id as customer_id,
                        sales.sale_id as sale_id,dining_facilities.name as table_name")
                    .joins("left join booking_orders on booking_orders.booking_id = bookings.booking_id")
                    .joins("left join dining_facilities on dining_facilities.id = bookings.dining_facility_id")
                    .joins("left join orders on orders.order_id = booking_orders.order_id")
                    .joins("left join sales on sales.sale_id = bookings.sale_id")
                    .where("(orders.status = 'new' or orders.status = 'billed') AND orders.date between #{ from } and #{ to }")

    # orders = Order.select("orders.order_id as order_id,sales.receipt_no,orders.status as order_status, sales.sale_status as sale_status,
    #            orders.order_id as order_id,sales.customer_id as sale_customer_id,orders.customer_id as order_customer_id
    #           ,bookings.booking_id,sales.sale_id as sale_id,dining_facilities.name as table_name")
    #       .joins("left join booking_orders on booking_orders.order_id = orders.order_id
    #       left join bookings on bookings.booking_id = booking_orders.order_id
    #       left join dining_facilities on dining_facilities.id = bookings.dining_facility_id
    #       left join order_items on order_items.order_id = orders.order_id
    #       left join sale_orders on sale_orders.order_id = orders.order_id
    #       left join sales on sales.sale_id = sale_orders.sale_id")
    #       .where("(orders.status = 'new' or orders.status = 'billed')")
    #       .group("orders.order_id")
          # For PG
          # .where("dining_facilities.is_active=? and orders.date between ? and ?",true,from,to)
          # .group("orders.order_id,order_items.order_items_id,dining_facilities.name,sales.receipt_no,bookings.booking_id,sales.sale_id,orders.customer_id")

  end

  def self.search(filter,from=nil,to=nil)
    if filter.blank?
        keyword = ''
    else
        keyword = "order_id LIKE ? OR status LIKE ? OR order_type LIKE ? OR source='#{filter}'","%#{filter}%","%#{filter}%","%#{filter}%"
    end

    if from.present? && to.present?
      order = Order.where("date BETWEEN ? AND ?", from, to)
      query = order.where(keyword)
    else
      where("order_id LIKE ? OR status LIKE ? OR order_type LIKE ? OR source='#{filter}'","%#{filter}%","%#{filter}%","%#{filter}%")
    end
  end

  def self.send_customer_view(booking)
    @status = ""
    @data_array = Array.new
    if(!booking.sale_id.nil?)
      sale = Sale.find(booking.sale_id)
         if sale.sale_status != "completed" && sale.sale_status != 'void'
           @data_array.push(sale)
           @status = "sale"
           @data_obj = sale
         end
    else

      @status = "order"
      booking.booking_orders.each do |booking_order|
        @data_obj = Order.find(booking_order.order_id)
        if (@data_obj.status == "new")
          @data_obj.order_items.each do |item|
            if !item.set_menu_items.nil?
              instance_item_sets = JSON.parse(item.set_menu_items)
               arr_instance_item_sets = Array.new
              instance_item_sets.each do |instance_item|
                item_instance_name = MenuItemInstance.find_by_item_instance_code(instance_item["item_instance_code"]).item_instance_name
                 arr_instance_item_sets.push(item_instance_name)
                item.price = item.price.to_f + instance_item["price"].to_f
              end
              item.set_menu_items = arr_instance_item_sets
            end
           @data_array.push(item)

          end
        end
      end
    end
    if ENV["SERVER_MODE"] == 'cloud'
      from = request.subdomain  + "." + request.domain
    else
      from = ""
    end
      ActionCable.server.broadcast "second_display_channel",data: @data_array,status:@status,from:from
  end

   #Process order items and send to order queue
  def self.process_order_queue(order_id, table_id, source)
    print_status = nil
    cup_status = nil

    #Send to background job for processing
    order        = Order.find(order_id)
    sidekiq      = Lookup.find_by_lookup_type("sidekiq")

    if ENV["SERVER_MODE"] != 'cloud'
      if Printer::PrinterWorker.printers.blank?
        msg = 'Print Error ! Please contact to service'
        ActionCable.server.broadcast "call_waiter_channel", table: msg, time:'print_error', from: ''
      end
    end

    if !sidekiq.nil?
      OrderQueueProcessorJob.perform_later(order_id, table_id)
    else
      if order
        oqs = OrderQueueStation.new
        oqs.process_order(order, table_id, source)
      end
      assign_order = AssignedOrderItem.assigned_order_item_by_job(order_id)
      ActionCable.server.broadcast "order_queue_station_channel", order: assign_order
    end
  end

  def self.check_cup_status(status)
    if status.include? "Active: active (running)" || "Active: active (exited)"   #"Cup Server is already running"
      return true
    end
    return false
  end

  def self.env
    return ENV["SERVER_MODE"]
  end

  private

  def generate_custom_id
    if self.order_id.nil?
      self.order_id = SeedGenerator.generate_id(self.class.name, "ODR")
    end
  end

  def set_order_date
    self.date = Time.current
  end

  def self.sync_order_records(orders)
    if !orders.nil?
      orders.each do |o|
        order = Order.find_by_order_id(o['order_id'])
        # unless Order.exists?(o['order_id'])
        if order.nil?
          order = Order.new
        end
        order.order_id = o['order_id']
        order.date = o['date']
        order.source = o['source']
        order.order_type = o['order_type']
        order.customer_id = o['customer_id']
        order.item_count = o['item_count']
        order.quantity_count = o['quantity_count']
        order.status = o['status']
        order.waiters = o['waiters']
        order.guest_info = o['guest_info']
        order.save
      end
      Rails.logger.debug '...... order sync completed .....'
    end
  end
  def self.send_message(phone, order_id,shop_name)
    url = "http://smspoh.com/api/http/send?key=5QfyN0OtGsFXnOqwtpVAGZCyPGP28nbX_Nm_oPsUw2ybq714T_951ycz3Ypl5URA&message=Your order "+order_id.to_s+" is ready to pick up ,thanks from "+shop_name.to_s+"&recipients="+ phone.to_s

    begin
    @result = HTTParty.get(url.to_str)
     rescue HTTParty::Error
       response = {status: false, message: "Can't open membership server "}
     rescue Net::OpenTimeout
       response = { status: false , message: "Can't open membership server "}
     rescue OpenURI::HTTPError
       response = { status: false, message: "Can't open membership server "}
     rescue SocketError
       response = { status: false, message: "Can't open membership server "}
     end
  end
end
