class Sale < ApplicationRecord
  self.primary_key = "sale_id"

  #primary key - need to be unique generated for multiple shops
  before_create :generate_custom_id
  #before_create :generate_receipt_no
  belongs_to :cashier, :optional => true
  belongs_to :customer, :optional => true
  has_many :sale_items
  has_many :sale_discount_items
  has_many :sale_discounts
  has_many :sale_taxes
  has_many :sale_payments
  has_many :sale_orders
  has_many :bookings

  scope :open_invoices, -> { where("sale_status = 'new' and receipt_date BETWEEN '#{DateTime.now.utc.end_of_day}' AND '#{DateTime.now.utc.beginning_of_day}'") }
  scope :complete_sale, -> { where("sale_status = 'completed' and receipt_date BETWEEN '#{DateTime.now.utc.beginning_of_day}' AND '#{DateTime.now.utc.end_of_day}'") }

  REPORT_TYPE = {
  "daily" => 0,
  "monthly" => 1,
  "yearly" => 2
  }
  SALE_STATUS_OUTSTANDING = "outstanding"
  SALE_STATUS_COMPLETED = "completed"

  def generate_invoice_from_booking(booking_id, requested_by, cashier)
    booking = Booking.find(booking_id)
    status = false
    Rails.logger.debug "Booking -> " + booking.id.to_s
    if (booking)
      Rails.logger.debug "Booking -> Booking Order Count -> " + booking.booking_orders.count.to_s
      #get all order attached to this booking and combine into 1 invoice

      booking.booking_orders.each do |order|
        if booking.sale_id
          status, sale_id = generate_invoice_from_order(order.order_id, nil, booking, requested_by, cashier)
        else
          status, sale_id = generate_invoice_from_order(order.order_id, booking.sale_id, booking, requested_by, cashier)
        end
        booking.sale_id = sale_id
      end

        order = booking.booking_orders.take.order
        link_order_sale(order.id)

      return status, sale_id
    end
  end

  def generate_invoice_from_order (order_id, sale_id, booking, requested_by, cashier = nil)
    taxable = true
    #if sale_id is exsit and validate
    #add order to that invoice
    if (sale_id)
      self.find(sale_id)
    end
    Rails.logger.debug "Does it have Existing Sale -> [#{self.id.to_s}] - Status [#{self.sale_status}]"

    if self.sale_status == "void"
      return false, "Invoice is void. Cannot be edited"
    else
      #if this is new sale generate_receipt_no
      generate_receipt_no
      order = Order.find(order_id)

      #Default Tax - Values
      self.tax_type = "exclusive"

      # set cashier
      if cashier != nil
        self.cashier_id = cashier[0].id
        self.cashier_name = cashier[0].name
      else
        self.cashier_id = requested_by.id
        self.cashier_name = requested_by.name
      end

      # set waiter
      self.requested_by = requested_by.name

      self.requested_at = DateTime.now.utc.getlocal

      Rails.logger.debug "Order -> #{order.id} | order_status -> #{order.status}"
      if order
        self.customer_id = order.customer_id
        Rails.logger.debug "Order -> #{order.id} | Items Count -> #{order.order_items.count}"

        order.order_items.each do |item|
          add_item(item)
        end

        link_order_sale(order.id)

      end
      self.save!

      #compute sales summary
      compute

      #Update the order items that is billed
      order.update_items_status_to_billed(nil)
      order.status = "billed"
      order.save

      booking.sale_id = self.id
      booking.checkout_at = Time.now.utc.getlocal
      booking.checkout_by = requested_by.name
      booking.save

      return true, self.id
    end

      return false, nil

  end

  #This is when spilt bill is request - then we cannot link order to invoice
  #Cos there will be multiple orders - and items are spilt from there.
  #Unless order is spilt by then it is possible.
  def generate_invoice_by_items (items, requested_by)
    taxable = true
    self.requested_by = requested_by
    self.requested_at = DateTime.now.utc.getlocal

    items.each do |item|
      add_item(item)

      #this will result in multiple orders belonging in multiple invoices - because of spilt invoices.
      link_order_sale(item.order_id, taxable)
    end

    #Update item status as billed
    order.update_items_status_to_billed(items)

    status = self.save!
    return status, self.id

  end

  def add_item (item)
    #check if the item is on promotion

    #save sale_audit
    sale_item = SaleItem.new

    #pull
    sale_item.product_code = item.item_code
    sale_item.product_name = item.item_name
    sale_item.product_alt_name = item.alt_name
    sale_item.account_id = item.account_id
    sale_item.remark = item.remark

    sale_item.qty = item.qty
    sale_item.unit_price = item.price
    sale_item.taxable_price = item.price
    sale_item.is_taxable = item.taxable

    sale_item.price = sale_item.qty * sale_item.unit_price

    self.sale_items << sale_item
  end


  def update_item (item)
    #save sale_audit

  end

  def apply_item_discount (item_code, discount_type, discount_amount)

  end

  def apply_discount (discount_type, discount_code)
    #save action to sale_audit
  end

  def void_sales (void_by, reason, approval_code, request_by)
    #save sale_audit
    self.sale_status = "void"
  end

  #compute - invoice total
  def compute
    sales_items = self.sale_items

    #Computation Fields
    subtotal_price = 0
    total_taxable = 0
    rounding_adjustment = 0

    sales_items.each do |item|
      #compute each item and added to total
      subtotal_price = subtotal_price + item.price
      total_taxable = total_taxable + (item.taxable_price * item.qty)
    end

    apply_tax (total_taxable)
    self.total_amount = subtotal_price
    self.total_discount = total_discount
    self.grand_total = (self.total_amount - self.total_discount) + self.total_tax
    #compute rounding adjustment
    adjust_rounding

    self.save!

  end

  #compute - invoice total
  def compute_by_sale_items(sale_id, sale_itemss, total_discount)
    sale = Sale.find(sale_id)
    sales_items = sale_itemss

    #Computation Fields
    subtotal_price = 0
    total_taxable = 0
    rounding_adjustment = 0

    sales_items.each do |item|
      #compute each item and added to total
      subtotal_price = subtotal_price + item.price
      total_taxable = total_taxable + (item.taxable_price * item.qty)
    end

    compute_tax(sale, total_taxable, total_discount)
    sale.total_amount = subtotal_price
    sale.total_discount = total_discount
    sale.grand_total = (sale.total_amount - sale.total_discount) + sale.total_tax
    #compute rounding adjustment
    # adjust_rounding

    sale.save!

  end

  def compute_without_void
    sales_items = self.sale_items

    #Computation Fields
    subtotal_price = 0
    total_taxable = 0
    rounding_adjustment = 0

    sales_items.each do |item|
      if item.remark != 'void'
        #compute each item and added to total
        subtotal_price = subtotal_price + item.price
        total_taxable = total_taxable + item.taxable_price
      end
    end

    apply_tax (total_taxable)
    self.total_amount = subtotal_price
    self.total_discount = total_discount
    self.grand_total = (self.total_amount - self.total_discount) + self.total_tax
    #compute rounding adjustment
    adjust_rounding

    self.save!
  end

  # Tax Re-Calculte
  def compute_tax(sale, total_taxable, total_discount = 0)
    #if tax is not apply create new record
    SaleTax.where("sale_id='#{sale.sale_id}'").find_each do |existing_tax|
      #delete existing and create new
      existing_tax.delete
    end

    total_tax_amount = 0
    #tax_profile - list by order_by
    tax_profiles = TaxProfile.all.order("order_by asc")

    # #Creat new tax records
    tax_profiles.each do |tax|
      sale_tax = SaleTax.new(:sale => sale)
      sale_tax.tax_name = tax.name
      sale_tax.tax_rate = tax.rate
      #include or execulive
      # sale_tax.tax_payable_amount = total_taxable  * tax.rate

      # substract , to give after discount
      total_taxable = total_taxable - total_discount
      sale_tax.tax_payable_amount = total_taxable  * tax.rate / 100
      #new taxable amount is standard rule for step by step
      # total_taxable = total_taxable + sale_tax.tax_payable_amount

      sale_tax.inclusive = tax.inclusive
      sale_tax.save

      total_tax_amount = total_tax_amount + sale_tax.tax_payable_amount
    end

    sale.total_tax = total_tax_amount
  end

  # Tax Calculate
  def apply_tax(total_taxable)
    #if tax is not apply create new record
    # self.sale_taxes.each do |existing_tax|
    #   #delete existing and create new
    #   existing_tax.delete
    # end

    #if tax is not apply create new record
    SaleTax.where("sale_id='#{self.sale_id}'").find_each do |existing_tax|
      #delete existing and create new
      existing_tax.delete
    end

    total_tax_amount = 0
    #tax_profile - list by order_by
    tax_profiles = TaxProfile.all.order("order_by asc")

    # #Creat new tax records
    tax_profiles.each do |tax|
      sale_tax = SaleTax.new(:sale => self)
      sale_tax.tax_name = tax.name
      sale_tax.tax_rate = tax.rate
      #include or execulive
      # sale_tax.tax_payable_amount = total_taxable  * tax.rate
      sale_tax.tax_payable_amount = total_taxable  * tax.rate / 100
      #new taxable amount is standard rule for step by step
      # total_taxable = total_taxable + sale_tax.tax_payable_amount

      sale_tax.inclusive = tax.inclusive
      sale_tax.save

      total_tax_amount = total_tax_amount + sale_tax.tax_payable_amount
    end

      self.total_tax = total_tax_amount

  end

  def product_get_unit_price(item_code)
    menu_item_hash =MenuItem.search_by_item_code(item_code)
    if (menu_instance_code)
        return menu_  item_hash[:item_instance_code], menu_item_hash[:price]
    end
      return nil,nil
  end

  def link_order_sale(order_id)
    #create if it doesn't exist
    saleOrder = SaleOrder.where("sale_id=? and order_id=?", self.id, order_id).take

    if saleOrder.nil?
      sale_order = SaleOrder.new
      sale_order.create_sale_order(self.id, order_id)
    end
    # if (SaleOrder.where("sale_id = #{self.id} and order_id=#{order_id}").nil?)
    #   SaleOrder.create(:sale_id => self.id, :order_id => order_id)
    # end
    #dosomrting here
    #puts Time.now.format(":short")
  end

  def adjust_rounding
    self.grand_total
    self.rounding_adjustment = 0.00
  end

  #Generate new Receipt No when it is not assigned
  def generate_receipt_no
    #Date-Shift-
    if self.receipt_no.nil?
      prefix = DateTime.now().utc
      #self.receipt_no = prefix.to_s + "/" + self.shit_id.to_s + "/" + SeedGenerator.new_receipt_no().to_s
      self.receipt_no = prefix.strftime("%Y%m%d") + "-" + SeedGenerator.new_receipt_no().to_s

      self.receipt_date = prefix
      Rails.logger.debug "Receipt No #{self.receipt_no} | Date #{ self.receipt_date.to_s}"
    end
  end

  def self.search(search)
    if search
      # find(:all, :conditions => ['name LIKE ? OR contact_no LIKE ?', "%#{search}%", "%#{search}%"])
      where("receipt_no LIKE ?", "%#{search}%",)
    else
      find(:all)
    end
  end

  def self.get_rounding_adjustment(num)
    ## 0 -- 25 -- 50 -- 75 -- 100
    # if get_rounded_amt == true
      value = 0

      num = num.to_f.round
      get_last_no = num.to_s.last(2).to_f
      if get_last_no.between?(0,25)
        ## down to 0
        num -= get_last_no
      else
         if get_last_no.between?(26,50)
           ## up to 50
           value = 50 - get_last_no.to_f
           num += value
           puts 'up to 50'
         else
           if get_last_no.between?(51, 75)
             ## down to 50
             value = get_last_no.to_f - 50
             num -= value
             puts 'down to 50'
           else
             ## up to 100
             value = 100 - get_last_no.to_f
             num += value
             puts 'up to 100'
           end
         end
      end
    # end
    return num
  end


  def self.daily_sales_list(from,to)
    payments_total = Sale.select("CAST((CONVERT_TZ(sales.receipt_date,'+00:00','+06:30')) AS DATE) as sale_date,
     SUM(case when (sale_payments.payment_method='mpu') then sale_payments.payment_amount else 0 end) as mpu_amount,
     SUM(case when (sale_payments.payment_method='master') then sale_payments.payment_amount else 0 end) as master_amount,
     SUM(case when (sale_payments.payment_method='visa') then sale_payments.payment_amount else 0 end) as visa_amount,
     SUM(case when (sale_payments.payment_method='jcb') then sale_payments.payment_amount else 0 end) as jcb_amount,
     SUM(case when (sale_payments.payment_method='paypar') then sale_payments.payment_amount else 0 end) as paypar_amount,
     SUM(case when (sale_payments.payment_method='cash') then sale_payments.payment_amount else 0 end) as cash_amount,
    SUM(case when (sale_payments.payment_method='credit') then sale_payments.payment_amount else 0 end) as credit_amount,
    SUM(case when (sale_payments.payment_method='foc') then sale_payments.payment_amount else 0 end) as foc_amount")
  .joins("join (select * from sale_payments group by sale_payments.sale_id, sale_payments.payment_method) sale_payments on sale_payments.sale_id = sales.sale_id")
  .where("sale_status = ? AND sales.receipt_date between ? and ? AND total_amount != 0", 'completed', from, to)
  .group("DATE_FORMAT((CONVERT_TZ(sales.receipt_date,'+00:00','+06:30')),'%Y-%m-%d')")

    daily_total = Array.new

    payments_total.each do |pay|
      sale_date = pay.sale_date
      diff_time = payments_total.first.sale_date.beginning_of_day.utc - from
      diff = diff_time % 86400
      from_date = sale_date.beginning_of_day.utc - diff
      to_date = sale_date.end_of_day.utc - diff

      total_sale = Sale.select("IFNULL(SUM(case when (sale_status='completed') then grand_total else 0 end),0) as grand_total,
                                IFNULL(SUM(case when (sale_status='completed') then old_grand_total else 0 end),0) as old_grand_total,
                                IFNULL(SUM(case when (sale_status='completed') then total_discount else 0 end),0) as total_discount,
                                IFNULL(SUM(case when (sale_status='void') then grand_total else 0 end),0) as void_amount,
                                IFNULL(SUM(case when (sale_status='completed') then rounding_adjustment else 0 end),0) as rounding_adj")
                            .where("(sale_status = ? OR sale_status = ?) AND receipt_date between ? and ? AND total_amount != 0", 'completed', 'void', from_date, to_date)

      total_sale.each do |sale|
        grand_total = sale.grand_total
        old_grand_total = sale.old_grand_total
        total_discount = sale.total_discount
        void_amount = sale.void_amount
        total = {:sale_date => pay.sale_date,
                 :mpu_amount => pay.mpu_amount,
                 :master_amount => pay.master_amount,
                 :visa_amount => pay.visa_amount,
                 :jcb_amount => pay.jcb_amount,
                 :paypar_amount => pay.paypar_amount,
                 :cash_amount => pay.cash_amount,
                 :credit_amount => pay.credit_amount,
                 :foc_amount => pay.foc_amount,
                 :total_discount => total_discount,
                 :grand_total => grand_total,
                 :old_grand_total => old_grand_total,
                 :void_amount => void_amount,
                   :rounding_adj => sale.rounding_adj}
        daily_total.push(total)
      end

    end
    return daily_total
  end

def self.get_by_range_by_saleitems(from,to,status,report_type)

      query = Sale.select("
                          mi.item_code as code,(SUM(i.qty) * i.unit_price) as grand_total,
                          SUM(i.qty) as total_item," +
                           " i.unit_price as unit_price,
                           mi.name as product_name,
                           mc.name as menu_category_name,
                           mc.id as menu_category_id ")
                          .group('mi.id')
                          .order("mi.menu_category_id")

      query = query.joins("JOIN sale_items i ON i.sale_id = sales.sale_id
                            JOIN menu_items mi ON i.product_code = mi.item_code" +
                            " JOIN menu_categories mc ON mc.id = mi.menu_category_id
                            JOIN employees ea ON ea.id = sales.cashier_id")


      query = query.where("receipt_date between ? and ? and sale_status=?",from,to,status)



    case report_type.to_i
       when REPORT_TYPE["daily"]
        return  query
       when REPORT_TYPE["monthly"]

        return  query.group("MONTH(date)")
      when REPORT_TYPE["yearly"]
         return query.group("YEAR(date)")
     end

end


def self.get_by_shiftsales(from,to)
    return ShiftSale.where("(shift_started_at between ? and ? OR shift_closed_at between ? and ? )", from, to, from, to)
end

# def self.get_by_shiftsales(employee,from,to)
#     shift_sales = ShiftSale.select('shift_sales.id, cs.name as cashier_station_name, shift_sales.shift_started_at as opening_date, shift_sales.shift_closeed_at as closing_date')
#                             .joins(" INNER JOIN cashier_terminals cs ON cs.id = shift_sales.cashier_terminal_id")
#                             .where("shift_sales.employee_id = ? and (shift_sales.shift_started_at between ? and ? OR shift_sales.shift_closeed_at between ? and ? )", employee, from, to, from, to)
#                             .order("shift_sales.id DESC")

#     sale_arr = Array.new
#     shift_sales.each do |shift|
#       all_total= Sale.select("grand_total,sr.payment_method, sr.payment_amount, rounding_adjustment")
#                       .joins("join employees e on sales.cashier_id = e.id join shift_sales sh on sh.id = sales.shift_sale_id")   
#                       .joins("INNER JOIN (select * from sale_payments group by sale_payments.sale_id, sale_payments.payment_method) sr ON sr.sale_id = sales.sale_id")
#                      .where("sales.shift_sale_id =? and sales.sale_status = 'completed' and sales.total_amount != 0",shift.id)
                     
#       void = Sale.select("SUM(sales.grand_total) AS grand_total")
#                       .joins("join shift_sales sh on sh.id = sales.shift_sale_id")
#                       .where('sales.sales_status = "void" and sales.total_amount != 0 and sales.shift_sale_id = ?', shift.id)
#                       .sum(:grand_total)
#       cash = all_total.select('sr.payment_type')
#                       .where('sr.payment_type = "cash"')
#                       .sum(:amount)

#       credit = all_total.where('sr.payment_type = "credit"')
#                       .sum(:amount)
                      
#       accept_credit = all_total.select('ci.amout')
#                       .joins("INNER JOIN credit_items ci ON ci.sale_id = sales.id")
#                       .where('sr.payment_type = "credit"')
#                       .sum(:amout)                      
                      

                      
#       foc = all_total.where('sales.payment_type = "foc" and sales.sales_status = "completed"')
#                     .sum(:grand_total)
                      
#       card = all_total.select('payment_type')
#                       .where('sr.payment_type = "card"')
#                       .sum(:amount).to_f 
                      
#       rounding_adj = all_total.sum(:rounding_adjustment)

#       discount = all_total.sum(:discount_amount)
#       void = void.nil? ? 0 : void
#       cash = cash.nil? ? 0 : cash
#       credit = credit.nil? ? 0 : credit
#       foc = foc.nil? ? 0 : foc
#       card = card.nil? ? 0 : card
#       accept_credit = accept_credit.nil? ? 0 : accept_credit
#       # all_payments = void.to_d + cash.to_d + credit.to_d + foc.to_d + card.to_d + accept_credit.to_d
#       all_payments = cash.to_d + credit.to_d + foc.to_d + card.to_d + accept_credit.to_d
#       payments = { void_amount: void, cash_amount: cash, credit_amount: credit,accept_credit_amount: accept_credit, foc_amount: foc, card_amount: card, grand_total: all_payments , cashier_station_name: shift.cashier_station_name, opening_date: shift.opening_date, closing_date: shift.closing_date, rounding_adj: rounding_adj }

#       sale_arr.push(payments)
#     end

#     return sale_arr            
#   end

  def get_cash_amount
    cash = 0.0
    self.sale_payments.each do |pay|
        if pay.payment_method == 'cash'
          cash = pay.payment_amount
        end
    end
    return cash
  end

  def get_credit_amount
    credit = 0.0
    self.sale_payments.each do |pay|
        if pay.payment_method == 'creditnote'
           credit = pay.payment_amount
        end
    end
    return credit
  end

  def get_other_amount
    other = 0.0
    self.sale_payments.each do |pay|
        if pay.payment_method != 'cash' && pay.payment_method != 'creditnote'
          other += pay.payment_amount
        end
    end
    return other
  end

  def get_commerical_tax
    tax = 0.0
     self.sale_taxes.each do |taxobj|
        if taxobj.tax_name == "Commerical Tax"
           tax += taxobj.tax_payable_amount
        end
     end
     return tax
  end

  private

  def generate_custom_id
    self.sale_id = SeedGenerator.generate_id(self.class.name, "SAL")
  end

  def self.get_receipt_no_list(from,to)
    sale = Sale.where("sale_status=? and receipt_date between ? and ?","completed",from,to)
  end
end
