class SaleItem < ApplicationRecord
  include NumberFormattable
  self.primary_key = "sale_item_id"

  #primary key - need to be unique generated for multiple shops
  before_create :generate_custom_id

  belongs_to :sale
  has_many :product_commissions

  #compute items - discount, tax, price_change
  def compute_item
  end

  before_validation :round_to_precision
  after_update :update_stock_journal
  after_save :update_stock_journal_set_item

  has_one  :discount_item, -> { where(status: 'Discount') }, class_name: 'SaleItem', foreign_key: 'parent_id'
  has_one  :foc_item, -> { where(status: 'foc') }, class_name: 'SaleItem', foreign_key: 'parent_id'
  has_one  :void_item, -> { where(status: 'void') }, class_name: 'SaleItem', foreign_key: 'parent_id'

  enum discount_type: { nett: 'nett', percentage: 'percentage' }

  # Add Sale Items
  def self.add_sale_items(sale_items)
    sale_items.each do|saleitemObj|
      @newsaleitem = SaleItem.new
      @newsaleitem = saleitemObj.dup
      @newsaleitem.qty = saleitemObj.qty * (-1)
      @newsaleitem.unit_price = saleitemObj.unit_price * (-1)
      @newsaleitem.taxable_price = (saleitemObj.unit_price * saleitemObj.qty) * (-1)
      @newsaleitem.price = (saleitemObj.unit_price * saleitemObj.qty) * (-1)
      @newsaleitem.product_name = saleitemObj.product_name + ' (FOC)'
      @newsaleitem.save
    end
  end

  def self.sync_sale_item_records(sale_items)
    if !sale_items.nil?
      sale_items.each do |si|
        sale_item = SaleItem.find_by_sale_item_id(si['sale_item_id'])
        # unless SaleItem.exists?(si['sale_item_id'])
        if sale_item.nil?
          sale_item = SaleItem.new
        end
        sale_item.sale_item_id = si['sale_item_id']
        sale_item.sale_id = si['sale_id']
        sale_item.menu_category_code = si['menu_category_code']
        sale_item.menu_category_name = si['menu_category_name']
        sale_item.product_code = si['product_code']
        sale_item.product_name = si['product_name']
        sale_item.product_alt_name = si['product_alt_name']
        sale_item.item_instance_code = si['item_instance_code']
        sale_item.account_id = si['account_id']
        sale_item.status = si['status']
        sale_item['remark'] = si['remark']
        sale_item['qty'] = si['qty']
        sale_item['unit_price'] = si['unit_price']
        sale_item['taxable_price'] = si['taxable_price']
        sale_item['price'] = si['price']
        sale_item['is_taxable'] = si['is_taxable']
        sale_item.save
      end
      Rails.logger.debug '....... Sale Item sync completed ......'
    end
  end

  def self.update_existing_item(qty, item, sale_id, type, item_price, price)
      # Original Item to add remark
      item.status = type
      item.save
      menu_category = MenuCategory.get_menu_category(item.product_code) #get menu category for menu items

      # sale_item = SaleItem.new
      # sale_item.menu_category_code = menu_category.code ? menu_category.code : nil
      # sale_item.menu_category_name = menu_category.name
      # sale_item.product_code = item.product_code
      # sale_item.item_instance_code = item.item_instance_code
      # sale_item.product_name = item.product_name + " (#{type.upcase})"
      # sale_item.product_alt_name = item.product_alt_name
      # sale_item.account_id = item.account_id
      # sale_item.status = type
      # sale_item.remark = type
      # if type == "foc" || type == "promotion" || type == "void" || type == "waste" || type == "spoile"
      #   sale_item.qty = qty * (-1)
      # else
      #   sale_item.qty = qty
      # end

      # sale_item.unit_price = item_price # * (-1)
      # sale_item.taxable_price = (price) * (-1)
      # sale_item.price = (price) * (-1)

      # sale_item.is_taxable = 1
      # sale_item.sale_id = sale_id
      # sale_item.save
      if type != "foc"
        sale =  Sale.find(sale_id)
        sale.compute_by_sale_items(sale.total_discount)
      end
  end

  def self.get_order_items_details(sale_id)
    order_details = SaleItem.select("sales.total_tax as tax_amount, sales.grand_total as grand_total_amount , sales.total_discount as discount_amount,sales.receipt_date as receipt_date,
                      sales.cashier_name,sales.receipt_no,sale_items.product_name as item_name,sale_items.qty,sale_items.price,sale_items.unit_price as total_price")
                   .joins("left join sales on sales.sale_id = sale_items.sale_id")
                   .where("sale_items.sale_id=?",sale_id)

    # sale_orders = SaleOrder.where("sale_id=?",sale_id)
    # if sale_orders
    #    sale_orders.each do |sale_order|
    #     order_details = SaleItem.select("sales.total_discount as discount_amount,DATE_FORMAT(sales.receipt_date,'%Y-%m-%d %h:%m') as receipt_date,sales.cashier_name,sales.receipt_no,sale_items.product_name as item_name,sale_items.qty,sale_items.price,sale_items.unit_price as total_price")
    #        .joins("left join sales on sales.id = sale_items.sale_id")
    #        .where("sale_items.sale_id=?",sale_order.sale_id)
    #     return order_details
    #   end
    # else
    #   return false
    # end
  end

  # Get Prices for each accounts (eg: food, beverage)
  def self.calculate_price_by_accounts(sale_items)
    price_accounts = []
    Account.all.each do |a|
      account_price = {:name => a.title, :price => 0}

      # Check for actual sale items
      sale_items.each do |si|
        if si.account_id == a.id
          account_price[:price] = account_price[:price] + si.price
        end
      end
      price_accounts.push(account_price)
    end

    return price_accounts
  end

  # Get discount Prices for each accounts (eg: food, beverage)
  def self.get_discount_price_by_accounts(sale_items)
    discount_accounts = []
    Account.all.each do |a|
      discount_account = {:name => a.title, :price => 0}
      # Check for actual sale items
      sale_items.where("status = 'Discount'").each do |si|
        if si.account_id == a.id
          discount_account[:price] = (discount_account[:price].abs + si.price.abs) * (1)
        end
      end
      discount_accounts.push(discount_account)
    end

    return discount_accounts
  end

 # Calculate rebate_by_account
  def self.calculate_rebate_by_account(sale_items)
    rebateacc =  Account.where("rebate=?",true)
    price = 0
    rebate_arr = []
    rebateacc.each do |a|
      account_price = {:type => a.title, :amount => 0}

      # Check for actual sale items
      sale_items.each do |si|
        if si.account_id == a.id
          account_price[:amount] = account_price[:amount] + si.price
           price = price + si.price
        end

      end
      rebate_arr.push(account_price)
    end

    return price,rebate_arr
  end

  # get food price or beverage price for item
  def self.get_rebate_price(sale_item_id,rebateacc)
    price = 0
    type = ''
    item=SaleItem.select("sale_items.price , menu_items.account_id")
                          .joins("left join menu_items on menu_items.item_code = sale_items.product_code")
                          .where("sale_items.sale_item_id=?", sale_item_id.to_s)

    rebateacc.each do |i|
      if item[0].account_id == i.id
        price = item[0].price
        type  = i.title
      end
    end

    return price,type
  end

   # Get Prices for each accounts (eg: food, beverage)
  def self.calculate_other_charges(sale_items)
    total = 0
      # Check for actual sale items
      sale_items.each do |si|
        if si.product_code == "Other Charges" && si.item_instance_code == nil
          total = total + si.price
        end
      end

    return total
  end

  # def self.get_overall_discount(sale_id)
  #   price = 0.0
  #   item=SaleItem.where("product_code=?", sale_id)
  #
  #   item.each do|i|
  #     price += i.price
  #   end
  #
  #   return price
  # end

  def self.get_all_sale_items(sale_id)
    sale_items = SaleItem.select("sale_id,product_code,item_instance_code,
      product_name,product_alt_name,account_id,status,remark,
      SUM(qty) as qty,
      unit_price,
      taxable_price,
      SUM(price) as price,
      is_taxable")
              .where("sale_id = ?",sale_id)
              .order("product_name asc")
              .group("status,product_name,item_instance_code,unit_price")
    return sale_items
  end

  # Loader Service SFTP Start
  # Detail Sale Data
  def self.get_detail_sale_data(transaction_date)
    query = SaleItem.select("
        sale_items.sale_item_id as id,
        sale_items.sale_id as parent_id,
        s.receipt_no as check_num,
        s.receipt_date as business_date,
        s.receipt_date as transaction_date,
        '' as item_seq,
        sale_items.menu_category_code as category_code,
        sale_items.menu_category_name as category_name,
        '' as sub_category_code,
        '' as sub_category_name,
        '' as report_group_code,
        '' as report_group_name,
        sale_items.product_code as item_id,
        sale_items.product_name as item_name,
        sale_items.qty as qty,
        CASE
          WHEN s.sale_status = 'completed' OR s.sale_status = 'void' THEN 'Sales'
            WHEN s.sale_status = 'waste' THEN 'Waste'
            WHEN s.sale_status = 'spoile' THEN 'Spoil'
        END as transaction_type,
        sale_items.price as gross_sales,
        '' as discount_code,
        CASE
          WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0
        END as discount_amt,
        (sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END)) as sales,
        ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END))/21) as tax_amt,
        '' as service_charges,
        ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END)) - ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END))/21)) as net_sales,
        '0' as is_set_item,
        '0' as is_staff_meal,
        '0' as is_raw_wastage,
        '0' as is_semi_wastage,
        CASE WHEN s.sale_status = 'waste' THEN 1 ELSE 0 END as is_wastage,
        CASE WHEN s.sale_status = 'spoile' THEN 1 ELSE 0 END as is_spoilage,
        '0' as is_sampling,
        '1' as tax_able,
        CASE
          WHEN s.sale_status = 'void' THEN 1 ELSE 0
        END as is_void
      ")
      .joins("LEFT JOIN sales s ON s.sale_id = sale_items.sale_id")
      .joins("LEFT JOIN sale_items i ON sale_items.sale_id = i.sale_id AND sale_items.item_instance_code = i.item_instance_code AND i.status = 'Discount' AND sale_items.qty = abs(i.qty)")
      .where("DATE(s.receipt_date) = ? AND s.sale_status != 'void' AND (sale_items.status NOT IN('Discount', 'void','foc') OR sale_items.status IS NULL)", transaction_date)
      .order("s.receipt_no")
  end

  # Loader Service SFTP End

  protected
  def self.generate_ids(count = 1)
    SeedGenerator.generate_ids(self.name, "SLI", count)
  end

  private
  def generate_custom_id
    if self.sale_item_id.nil?
      self.sale_item_id = SeedGenerator.generate_id(self.class.name, "SLI")
    end
  end

  def round_to_precision
    unit_price_fraction = self.unit_price % 1
    price_fraction = self.unit_price % 1
    # is_dining_charge = self.menu_category_code == 'DingingCharge'

    if self.unit_price != self.unit_price_was || self.price != self.price_was
      if unit_price_fraction > 0 || price_fraction > 0
        if ['Discount', 'promotion'].include?(self.status)
          self.unit_price = self.unit_price.round(precision)
          self.price = (self.unit_price * self.qty).round(precision)
          self.taxable_price = self.price
        end
      end
    end
  end

  def update_stock_journal
    unless MenuItemInstance.where("item_instance_name <> ''").pluck(:item_instance_code).include?(self.item_instance_code)
      is_void = self.status == "void" && self.status_before_last_save != "void"
      cancel_void = self.status_before_last_save == "void" && self.status.nil?
      is_edit = self.qty >= 0 && self.qty != self.qty_before_last_save
      is_foc =  self.status == "foc" && self.status_before_last_save != "foc"
      cancel_foc = self.status_before_last_save == "foc"

      if is_void or cancel_void or is_edit or is_foc or cancel_foc
        found, inventory_definition = InventoryDefinition.find_product_in_inventory(self)
        if found
          stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first
          unless stock.nil?
            check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first
            if is_void or cancel_void or is_edit
              if is_void
                qty = -self.qty
                remark = "void"
              elsif cancel_void
                qty = self.qty
                remark = "cancel void"
              elsif is_edit
                qty = self.qty - self.qty_before_last_save
                remark = "edit"
              end
              StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS)
              check_item.different = check_item.different + qty
              check_item.save
            else is_foc or cancel_foc
              qty = StockJournal.where(trans_ref: self.sale_item_id).sum("credit-debit")
              if order_item_id = self.sale.bookings.first.order_items.where(item_instance_code: self.item_instance_code, qty: self.qty + qty).select(:order_items_id).first.order_items_id
                if stock_journal = StockJournal.find_by_trans_ref(order_item_id)
                  if is_foc
                    stock_journal.update(remark: "foc")
                  elsif cancel_foc
                    stock_journal.update(remark: "cancel_foc")
                  end
                end
              end
            end
          end
        end
      end
    end
  end

  def update_stock_journal_set_item
    is_void = self.status == "void" && self.status_before_last_save != "void" && self.qty > 0
    cancel_void = self.status_before_last_save == "void" && self.status.nil?
    is_edit = self.qty >= 0 && self.qty != self.qty_before_last_save
    is_foc =  self.status == "foc" && self.status_before_last_save != "foc"
    cancel_foc = self.status_before_last_save == "foc"

    is_waste = self.status == "waste"
    is_spoile = self.status == "spoile"

    if MenuItemInstance.where("item_instance_name <> ''").pluck(:item_instance_code).include?(self.item_instance_code)
      if self.qty == 1 && self.qty != self.qty_before_last_save
        found, inventory_definition = InventoryDefinition.find_product_in_inventory(self)
        if found
          stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first
          unless stock.nil?
            check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first
            if self.qty.to_i >= 0
              qty = self.qty - self.qty_was
              if stock.balance.to_i >= qty
                Rails.logger.info ">> stock is greater than order qty"
                remark = "ok"
              else
                Rails.logger.info " << stock is less than order qty"
                remark = "out of stock"
              end
            end
            StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS)
            check_item.different = check_item.different - qty
            check_item.save
          else
            StockJournal.add_to_journal(self.item_instance_code, self.qty, 0, "out of stock", inventory_definition, self.id, StockJournal::SALES_TRANS)
          end
        end
      elsif is_void or cancel_void or is_edit
        if is_void
          qty = -self.qty
          remark = "void"
        elsif cancel_void
          qty = self.qty
          remark = "cancel void"
        elsif is_edit
          qty = self.qty - self.qty_before_last_save
          remark = "edit"
        end
        found, inventory_definition = InventoryDefinition.find_product_in_inventory(self)
        if found
          stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first
          unless stock.nil?
            check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first

            StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS)
            check_item.different = check_item.different + qty
            check_item.save
          end
        end
      elsif is_foc or cancel_foc
        qty = StockJournal.where(trans_ref: self.sale_item_id).sum("credit-debit")
        if stock_journal = StockJournal.where(trans_ref: self.sale_item_id, item_code: self.item_instance_code).order(id: :desc).first
          if is_foc
            stock_journal.update(remark: "foc")
          elsif cancel_foc
            stock_journal.update(remark: "cancel_foc")
          end
        end
      elsif is_waste or is_spoile
        found, inventory_definition = InventoryDefinition.find_product_in_inventory(self)
        if found
          if stock_journal = StockJournal.where(trans_ref: self.sale_item_id, item_code: self.item_instance_code)
            stock_journal.update(remark: self.status)
          end
        end
      end
    end
  end
end
