class SalesController < ApplicationController
  ##--- Abilities
  load_and_authorize_resource
  ##--- Breadcrum_rails
  add_breadcrumb I18n.t("breadcrumbs." + controller_name), :sales_path
  add_breadcrumb "Nueva " + I18n.t("breadcrumbs." + controller_name).singularize, :new_sale_path, only: :new
  add_breadcrumb "Detalle de la " + I18n.t("breadcrumbs." + controller_name).singularize, :sale_path, only: :show
  add_breadcrumb "Editar " + I18n.t("breadcrumbs." + controller_name).singularize, :edit_sale_path, only: :edit

  before_action :set_sale, only: [:show, :edit, :update, :destroy]
  before_action :get_filters, only: [:index, :show, :edit, :new, :sales_reserved]

  # GET /sales
  # GET /sales.json
  def index
    today = Date.current
    thirty_day_ago = today - 30
    @sales = current_user.usertype == 'A' ? Sale.includes(:customer, :user, :seller, :sales_details).where(date_sale: thirty_day_ago..today).where.not(saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.includes(:customer, :user, :seller).where(date_sale: thirty_day_ago..today).where.not(saletype: 2).order("created_at DESC")
  end

  def sales_reserved
    beg_of_month = Date.current.beginning_of_month
    end_of_month = Date.current.end_of_month
    @sales = current_user.usertype == 'A' ? Sale.where(saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.where(saletype: 2).order(" created_at DESC")
  end

  # GET /sales/1
  # GET /sales/1.json
  def show
    @payments = CashRegistersMove.where(sale_id: @sale.id, status: 1)
    # saca la devolucion si es que hay.
    @products_return = ProductsReturn.find_by(sale_id: @sale.id)
    @returned_prods_ids = Array.new
    @returned_prods_ids = @products_return.products_return_ins.pluck(:product_id) if @products_return.present?
  end

  # GET /sales/new
  def new
    @sale = Sale.new
    @sale.sales_details.new
    @general_public_id = Customer.find_by(is_public: 1).id
    @pre_sales = PreSale.where(user_id: current_user.id)
    @sale.saletype = 'cash'
    # se desactivan cuando tienen pre sales, para mantener la congruencia de los datos.
    @disabled_select = false
    @disabled_button = true
    @enable_radios = true
    @opened_cash_registers = Pointsale.find(current_user.pointsale_id).open_cash_registers.abiertas
    @sale.open_cash_register_id = session[:open_cash_register_id] || nil
    if @pre_sales.present?
      @sale.saletype = @pre_sales[0].sale_type
      @sale.customer_id = @pre_sales[0].customer_id
      @sale.open_cash_register_id = @pre_sales[0].open_cash_register_id
      @disabled_select = true
      @disabled_button = false
      @enable_radios = false
    end
  end

  # GET /sales/1/edit
  def edit; end

  # POST /sales
  # POST /sales.json
  # rubocop:disable Metrics/BlockLength
  def create
    respond_to do |format|
      @sale = Sale.new(sale_params)
      @pre_sales = PreSale.where(user_id: current_user.id)
      @sale.user_id = current_user.id
      @sale.open_cash_register_id = session[:open_cash_register_id]
      @sale.status = :notpaid
      @sale.expiration_date = Date.today + @pos_config.days_cancel_reserved if @sale.reserved?
      @sale.audit_comment = "Venta #{@sale.sale_code} por #{@sale.total} creada."

      if @sale.save
        # agregar detalles de la venta
        @pre_sales.each do |pre_sale|
          detail = SalesDetail.new(product_id: pre_sale.product_id, unit_price: pre_sale.unit_price, quantity: pre_sale.quantity, amount: pre_sale.amount, tax: pre_sale.tax, discount: pre_sale.discount, total: pre_sale.total, special_price_id: pre_sale.special_price_id, status: :active)
          @sale.sales_details << detail
          pre_sale.destroy
          # actualizar stock del producto
          stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
          next if stock_product.blank?
          if stock_product.stock.present?
            stock_product.stock -= detail.quantity
            stock_product.save
          else
            errors.add(:base, "No se tiene registrado el stock de alguno de los productos. Es necesario configurarlo antes de generar una venta")
            format.json { render json: @sale.errors.values, status: :unprocessable_entity }
          end
          # guardar en bitacora de inventario
          move = InventoriesMove.new(product_id: detail.product_id, sale_id: @sale.id, quantity: detail.quantity, move_type: "outgoing", reason: "sale")
          move.save
        end
        # dependiendo el tipo de venta: contado/credito determina que hacer
        if @sale.cash?
          format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
        elsif @sale.credit?
          credit = Credit.new(customer_id: @sale.customer_id, pointsale_id: current_user.pointsale_id, sale_id: @sale.id, total: @sale.total, rest: @sale.total, status: "active", credit_note: @sale.credit_note.present? ? @sale.credit_note : "")
          credit.save
          flash[:success] = "Venta a crédito registrada al cliente: #{@sale.customer.nick_name} por $ #{@sale.total}"
          format.js { render 'create_credit_sale' }
        elsif @sale.reserved?
          format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
        end
      else
        format.js
        format.json { render json: @sale.errors, status: :unprocessable_entity }
      end
    end
  end
  # rubocop:enable Metrics/BlockLength

  # PATCH/PUT /sales/1
  # PATCH/PUT /sales/1.json
  def update
    respond_to do |format|
      if @sale.update(sale_params)
        format.html { redirect_to @sale, notice: 'Venta modificada.' }
        format.json { render :show, status: :ok, location: @sale }
      else
        format.html { render :edit }
        format.json { render json: @sale.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /sales/1
  # DELETE /sales/1.json
  # rubocop:disable Metrics/BlockLength
  def destroy
    respond_to do |format|
      @sale.audit_comment = "Venta #{@sale.sale_code} cancelada."
      if @sale.update_attributes(status: :cancelled)
        # rubocop:disable Metrics/BlockNesting
        if @sale.reserved?
          return_cash = params[:return_cash]
          if return_cash == 'true'
            moves = CashRegistersMove.where(sale_id: @sale.id, move_type: 1)
            if moves.present?
              if moves[0].open_cash_register.closed?
                quantity_to_return = moves.sum(:quantity)
                new_cash_move = CashRegistersMove.new(open_cash_register_id: session[:open_cash_register_id], payment_method_id: PaymentMethod.find_by(isCash: 1).id, quantity: quantity_to_return, move_type: :egreso, sale_id: @sale.id, concept: :sale, status: :active)
                new_cash_move.skip_received_validation = true
                new_cash_move.save
              else
                moves.destroy_all
              end
            end
          end
        elsif @sale.cash?
          # checa si hay pagos de esta venta
          moves = CashRegistersMove.where(sale_id: @sale.id)
          # si la caja sigue abierta, solo elimina los moves, si ya corto, genera un egreso por esa cantidad
          if moves.present?
            if moves[0].open_cash_register.closed?
              new_cash_move = CashRegistersMove.new(open_cash_register_id: session[:open_cash_register_id], payment_method_id: PaymentMethod.find_by(isCash: true).id, quantity: moves.sum(:quantity), move_type: :egreso, sale_id: moves[0].sale_id, concept: :sale, ticket: moves[0].sale.sale_code, status: :active)
              new_cash_move.skip_received_validation = true
              new_cash_move.save
            else
              moves.destroy_all
            end
          end
        elsif @sale.credit?
          credit = Credit.find_by(sale_id: @sale.id, customer_id: @sale.customer_id)
          credit.update_attributes(status: :cancelled)
        end
        # rubocop:enable Metrics/BlockNesting
        @sale.sales_details.each do |detail|
          detail.update_attributes(status: :inactive)
          stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
          next if stock_product.blank?
          # sumarle al stock del producto
          stock = stock_product.stock + detail.quantity
          stock_product.update_attributes(stock: stock)
          # guardar en bitacora de inventario
          move = InventoriesMove.new(product_id: detail.product_id, sale_id: @sale.id, quantity: detail.quantity, move_type: "incoming", reason: (@sale.reserved? ? "sale_reserved_cancelled" : "sale_cancel"))
          move.save
        end
        format.html { redirect_to (@sale.reserved? ? sales_reserved_path : sales_url), warning: (@sale.reserved? ? "Apartado con folio #{@sale.sale_code} cancelado." : "Venta con folio #{@sale.sale_code} cancelado.") }
        format.json { head :no_content }
      end
    end
  end
  # rubocop:enable Metrics/BlockLength

  def find_sales_by_date
    if params[:begin_date].present? && params[:end_date].present?
      start_date = DateTime.parse(params[:begin_date])
      end_date = DateTime.parse(params[:end_date])
    else
      start_date = Date.today.beginning_of_month
      end_date = Date.today.end_of_month
    end
    @sales = current_user.usertype == 'A' ? Sale.includes(:customer, :user, :seller, :sales_details).where(date_sale: start_date..end_date).where.not(saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.includes(:customer, :user, :seller, :sales_details).where(date_sale: start_date..end_date).where.not(saletype: 2).order(" created_at DESC ")
    respond_to do |format|
      format.js
    end
  end

  def find_customer_sales_by_date
    if params[:begin_date].present? && params[:end_date].present?
      start_date = DateTime.parse(params[:begin_date])
      end_date = DateTime.parse(params[:end_date])
    else
      start_date = Date.today.beginning_of_month
      end_date = Date.today.end_of_month
    end
    cliente = params[:cliente]
    @sales = Sale.where(date_sale: start_date..end_date, customer_id: cliente).where.not(saletype: 2).order(" id DESC ")

    respond_to do |format|
      format.js { render action: "find_sales_by_date" }
    end
  end

  def find_reserved_sales_by_date
    if params[:begin_date].present? && params[:end_date].present?
      start_date = DateTime.parse(params[:begin_date])
      end_date = DateTime.parse(params[:end_date])
    else
      start_date = Date.today.beginning_of_month
      end_date = Date.today.end_of_month
    end
    @sales = current_user.usertype == 'A' ? Sale.includes(:customer, :user, :seller, :sales_details).where(date_sale: start_date..end_date, saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.includes(:customer, :user, :seller, :sales_details).where(date_sale: start_date..end_date).where(saletype: 2).order(" created_at DESC ")

    respond_to do |format|
      format.js
    end
  end

  def return_expired
    @sale = Sale.find(params[:sale_id])
    respond_to do |format|
      if @sale.update_attributes(status: :cancelled_by_expiration)
        @sale.sales_details.each do |detail|
          detail.update_attributes(status: :inactive)
          stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
          next if stock_product.blank?
          # sumarle al stock del producto
          stock = stock_product.stock + detail.quantity
          stock_product.update_attributes(stock: stock)
          # guardar en bitacora de inventario
          move = InventoriesMove.new(product_id: detail.product_id, sale_id: @sale.id, quantity: detail.quantity, move_type: "incoming", reason: "sale_expired")
          move.save
        end
        format.html { redirect_to sales_reserved_url, warning: "Productos reingresados al inventario del apartado #{@sale.sale_code}." }
        format.json { head :no_content }
      end
    end
  end

  def liquidate_reserve
    respond_to do |format|
      @sale = Sale.find(params[:sale_id])
      @payments = CashRegistersMove.where(sale_id: @sale.id, status: 1)
      format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
    end
  end

  def cancel_reserved_sale
    @sale = Sale.find(params[:sale_id])
  end

  def print_receipt
    respond_to do |format|
      sale = Sale.find(params[:sale_id])
      format.pdf do
        render pdf: "ticket_venta_#{sale.id}", template: "sales/receipt.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
      end
    end
  end

  def print_reserve_receipt
    # ticket para apartado
    respond_to do |format|
      sale = Sale.find(params[:sale_id])
      # debt = sale.cash_registers_moves.first.quantity
      debt = sale.cash_registers_moves.sum(:quantity)
      format.pdf do
        render pdf: "ticket_apartado_#{sale.id}", template: "sales/receipt_reserve.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, debt: debt }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
      end
    end
  end

  def print_credit_receipt
    # ticket para credito
    respond_to do |format|
      sale = Sale.find(params[:sale_id])
      debt = sale.customer.credits.activos.sum(:rest)

      format.pdf do
        render pdf: "ticket_credito_#{sale.id}", template: "sales/receipt_credit.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, debt: debt }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
      end
    end
  end

  def print_partial_payment_receipt
    # ticket para abonos a apartado
    respond_to do |format|
      sale = Sale.find(params[:sale_id])
      new_moves = CashRegistersMove.where(id: params[:new_moves_array])
      quantity = new_moves.sum(:quantity)
      format.pdf do
        render pdf: "ticket_abono_apartado_#{sale.id}", template: "sales/receipt_partial_payment.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, new_payments_quantity: quantity, moves: new_moves }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
      end
    end
  end

  def print_credit_payment_receipt
    # ticket para abonos a credito
    respond_to do |format|
      sale = Sale.find(params[:sale_id])
      deuda = params[:debts]
      cash_move = CashRegistersMove.where(sale_id: sale.id).last
      quantity = cash_move.quantity
      debt = Credit.where(customer_id: sale.customer_id).sum(:rest)

      format.pdf do
        render pdf: "ticket_abono_credito_#{sale.id}", template: "sales/receipt_credit_payment.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, new_payment: quantity, debt: debt, deuda: deuda, move: cash_move }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
      end
    end
  end

  def add_haggle
    @pre_sale = PreSale.find(params[:pre_sale])
    @haggle_percent = !current_user.pointsale.haggle_percent.blank? ? current_user.pointsale.haggle_percent : @pos_config.haggle_in_sale_percent
    @suggested_haggle = (@haggle_percent.to_f / 100) * @pre_sale.unit_price
  end

  def create_haggle
    respond_to do |format|
      @pre_sale = PreSale.find(params[:pre_sale])
      @haggle_percent = params[:haggle_percent].present? ? params[:haggle_percent].to_f : nil
      @haggle_quantity = params[:haggle_quantity].present? ? params[:haggle_quantity].to_f : nil

      if @haggle_percent.present?
        @pre_sale.haggle = (@haggle_percent / 100) * @pre_sale.total
      elsif @haggle_quantity.present?
        @pre_sale.haggle = @haggle_quantity
      end
      @pre_sale.get_totals
      if @pre_sale.save
        format.js
      end
    end
  end

  def find_sales_by_dates_or_code
    respond_to do |format|
      if params[:sale_code].present?
        @sales = Pointsale.find(current_user.pointsale_id).sales.activas.where(sale_code: params[:sale_code], saletype: 1).order("id DESC")
      else
        start_date = DateTime.parse(params[:start_date])
        end_date = DateTime.parse(params[:end_date])
        product_id = params[:product_id]
        @sales = product_id.present? ? Pointsale.find(current_user.pointsale_id).sales.activas.where(date_sale: start_date..end_date, saletype: 1).joins(:sales_details).where('sales_details.product_id = (?)', product_id).order("id DESC") : Pointsale.find(current_user.pointsale_id).sales.activas.where(date_sale: start_date..end_date, saletype: 1).joins(:sales_details).order("id DESC")
      end
      format.js
    end
  end

  def sales_per_month_report
    @pointsales = Pointsale.vigentes
  end

  def sales_per_month
    respond_to do |format|
      # start_date = DateTime.parse(params[:start_date])
      # end_date = DateTime.parse(params[:end_date])
      start_date = DateTime.parse(params[:start_date]).in_time_zone(Time.zone).beginning_of_day + 1.days
      end_date = DateTime.parse(params[:end_date]).in_time_zone(Time.zone).end_of_day + 1.days
      @cash_sales_total = 0
      @reserved_sales_total = 0
      @credit_sales_total = 0

      if params[:pointsale_id].present?
        @pointsale = Pointsale.find(params[:pointsale_id])
        @incomes_in_period = @pointsale.cash_registers_moves.joins(:sale).where(move_type: '1', created_at: start_date..end_date)
        @sales = @pointsale.sales.activas.where(created_at: start_date..end_date).order("id DESC")
      else
        @incomes_in_period = CashRegistersMove.joins(:sale).where(move_type: '1', created_at: start_date..end_date)
        @sales = Sale.activas.where(created_at: start_date..end_date).order("id DESC")
      end

      @sales_quantity = @sales.size
      @prods_total = SalesDetail.where("sale_id IN (?)", @sales.pluck(:id)).sum(:quantity).round
      # @cash_sales_income = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 1).pluck(:id)).where(created_at: start_date..end_date).sum(:quantity)
      # @reserved_sales_income = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 2).pluck(:id)).where(created_at: start_date..end_date).sum(:quantity)
      # @credit_sales_income = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 0).pluck(:id)).where(created_at: start_date..end_date).sum(:quantity)
      @cash_sales_income = @incomes_in_period.where('sales.saletype = 1').sum(:quantity)
      @reserved_sales_income = @incomes_in_period.where('sales.saletype = 2').sum(:quantity)
      @credit_sales_income = @incomes_in_period.where('sales.saletype = 0').sum(:quantity)
      @sales_income = @incomes_in_period.sum(:quantity)

      @cash_sales_total = @sales.where(saletype: 1).sum(:total)
      @reserved_sales_total = @sales.where(saletype: 2).sum(:total)
      @credit_sales_total = @sales.where(saletype: 0).sum(:total)
      @sales_total = @sales.sum(:total)
      format.js
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_sale
    @sale = Sale.find(params[:id])
  end

  def get_filters
    @current_page = params[:current_page].blank? ? 1 : params[:filter]
    @filter = params[:filter]
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def sale_params
    params.require(:sale).permit(:customer_id, :saletype, :amount, :tax, :discount, :total, :date_sale, :user_id, :seller_id, :sale_code, :credit_note, :open_cash_register_id)
  end
end
