class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.

  before_filter do
    resource = controller_path.singularize.tr('/', '_').to_sym
    method = "#{resource}_params"
    params[resource] &&= send(method) if respond_to?(method, true)
  end

  before_filter :set_pos_config
  around_filter :user_time_zone, if: :set_pos_config

  protect_from_forgery with: :exception

  ##--- Breadcrum_rails
  add_breadcrumb I18n.t("breadcrumbs.dashboard"), :root_path

  ##--- Restriccion para autentificacion
  before_action :authenticate_user!

  ##--- Notes boxes
  add_flash_types :success, :warning, :danger, :info

  ##--- Parametros permitidos para los usuarios
  before_action :configure_permitted_parameters, if: :devise_controller?

  ##--- Redireccionamiento para los permisos a modulos
  rescue_from CanCan::AccessDenied do |exception|
    redirect_to root_url, alert: exception.message
  end

  ##--- Funciones personalizadas
  def getcounties
    render json: SpmxCounty.where("state_id = ?", params[:state_id])
  end

  def find_products
    products =
      if params[:variants] == "true" && params[:query].include?(":")
        attributes = query_for_variants(params[:query])
        Product.name_sku_barcode_attribute_like(@product_name, attributes).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
      elsif params[:variants] == "true"
        Product.name_sku_barcode_like(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
      else
        Product.name_sku_barcode_like_sp(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
      end
    render json: products
  end

  def find
    query = params[:query]
    if query.include? ':' # search with attributes
      query_array = query.split(':')
      product_name = query_array[0]
      query_array.shift # delete the name of the product from the array to iterate the attributes
      attrs_query_string = ''
      query_array.each do |attribute|
        if attribute.present?
          attr_type = case attribute[0]
                      when 'c'
                        'colors'
                      when 't'
                        'sizes'
                      when 'e'
                        'styles'
                      end
          attribute[0] = "" # delete the attribute type character
          attrs_query_string.concat(" AND attributes_json::json->>'#{attr_type}' ilike '%#{attribute}%'")
        else
          next
        end
      end
    else
      product_name = query
    end

    render json: query.include?(":") ? Product.name_sku_barcode_attribute_like(product_name, attrs_query_string).limit(30).to_json(methods: [:small_img, :display_attributes]) : Product.name_sku_barcode_like(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes])
  end

  def query_for_variants(query)
    product_query =
      if query.include? ':' # search with attributes
        query_array = query.split(':')
        @product_name = query_array[0]
        query_array.shift # delete the name of the product from the array to iterate the attributes
        attrs_query_string = ''
        query_array.each do |attribute|
          next if attribute.nil?
          attr_type =
            case attribute[0]
            when 'c'
              'colors'
            when 't'
              'sizes'
            when 'e'
              'styles'
            end
          attribute[0] = "" # delete the attribute type character
          attrs_query_string.concat(" AND attributes_json::json->>'#{attr_type}' ilike '%#{attribute}%'")
        end
        attrs_query_string
      else
        query
      end
    product_query
  end

  # para special_prices
  def find_sp
    query = params[:query]
    product_name = query

    render json: Product.name_sku_barcode_like_sp(product_name).limit(30).to_json(methods: [:small_img])
  end

  def find_from_stock
    query = params[:query]

    location =
      if current_user.usertype == "S"
        Warehouse.find(current_user.warehouse_id).products
      else
        Pointsale.find(current_user.pointsale_id).products
      end
    attributes = query_for_variants(params[:query])
    consult =
      if query.include? ':'
        location.name_sku_barcode_attribute_like(@product_name, attributes)
      else
        location.name_sku_barcode_like(params[:query])
      end

    render json: consult.where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
  end

  def find_from_stock_by_pointsale
    if params[:pointsale_id].present?
      id = params[:pointsale_id][2, params[:pointsale_id].length]
      attributes = query_for_variants(params[:query])
      query = params[:query]
      location =
        if params[:pointsale_id].first == "P"
          Pointsale.find(id).products
        else
          Warehouse.find(id).products
        end
      consult =
        if query.include?(":")
          location.name_sku_barcode_attribute_like(@product_name, attributes)
        else
          location.name_sku_barcode_like(params[:query])
        end
      render json: consult.where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
    else
      render json: {}
    end
  end

  def get_subcategories
    render json: params[:category_id] != '0' ? Category.activos.where("parent_id = ?", params[:category_id]) : Category.activos.where('parent_id != 0')
  end

  def set_pos_config
    @pos_config = PosConfig.first
  end

  def user_time_zone(&block)
    Time.use_zone(@pos_config.time_zone, &block)
  end

  def delete_pre_sales
    PreSale.where(user_id: current_user.id).destroy_all
    respond_to do |format|
      format.json { head :no_content }
    end
  end

  def delete_pre_purchases
    PrePurchase.where(user_id: current_user.id).destroy_all
    # render head :no_content
    respond_to do |format|
      format.json { head :no_content }
    end
  end

  def delete_pre_transfers
    respond_to do |format|
      pre_transfers = PreTransfer.where(user_id: current_user.id)
      pre_transfers.each do |pre|
        # rubocop:disable Style/Next
        if pre.destroy
          stock = pre.origin_is_pointsale == 1 ? AvailableProduct.find_by(pointsale_id: pre.origin_id, product_id: pre.product_id) : WarehouseStock.find_by(warehouse_id: pre.origin_id, product_id: pre.product_id)
          stock.stock += pre.quantity
          stock.save
        end
        # rubocop:enable Style/Next
      end
      format.json { head :ok }
    end
  end

  def get_max_product_id
    render json: Product.maximum(:id).to_i.next
  end

  def get_max_purchaseid_by_pointsale
    prefix = Pointsale.find(params[:pointsale_id]).prefix
    next_id = Purchase.where(pointsale_id: params[:pointsale_id]).count.next

    render json: "#{prefix}-C-#{next_id}"
  end

  def get_max_purchaseid_by_warehouse
    prefix = Warehouse.find(params[:warehouse_id]).prefix
    next_id = Purchase.where(warehouse_id: params[:warehouse_id]).count.next

    render json: "#{prefix}-C-#{next_id}"
  end

  def get_next_sale_code
    pointsale = OpenCashRegister.find(params[:open_cash_register_id]).cash_register.pointsale
    next_id = pointsale.sales.count.next
    render json: "#{pointsale.prefix}-V-#{next_id}"
  end

  def get_next_expense_code
    code =
      if current_user.usertype == "A" || current_user.usertype == "SS"
        next_id = Expense.where("expense_code ilike ?", '%ADM%').count.next
        "ADM-E-#{next_id}"
      else
        pointsale = OpenCashRegister.find(params[:open_cash_register_id]).cash_register.pointsale
        next_id = pointsale.expenses.count.next
        "#{pointsale.prefix}-E-#{next_id}"
      end
    render json: code
  end

  def products_by_category_pointsale
    products = Array.new
    products_by_line = Array.new
    category_id = params[:category_id]
    id = params[:pointsale_id][2, params[:pointsale_id].length]

    categories = Category.find(category_id).self_and_descendents

    categories.each do |category|
      products_by_line += category.products
    end

    if params[:pointsale_id].first == 'P'
      Pointsale.find(id).products.each do |p|
        if products_by_line.include?(p)
          products << p
        end
      end
    else
      Warehouse.find(id).products.each do |p|
        if products_by_line.include?(p)
          products << p
        end
      end
    end
    render json: products
  end

  ## ======= invoice methods =======
  def check_directorys(t, move_type)
    unless Dir.exist?(Rails.public_path.join("invoice", "cfdi")) # carpeta invoice
      Dir.mkdir(Rails.public_path.join("invoice", "cfdi"))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi", t.strftime('%Y'))) # carpeta cfdi
      Dir.mkdir(Rails.public_path.join("invoice/cfdi", t.strftime("%Y")))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"))) # carpeta del año
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m")))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "xml")) # carpeta del mes
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}", "xml"))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "xml", move_type)) # carpeta de tipo de factura
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/xml", move_type))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "pdf")) # carpeta para pdf
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}", "pdf"))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "pdf", move_type))
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/pdf", move_type))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "qr"))
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}", "qr"))
    end
    unless Dir.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}", t.strftime("%m"), "qr", move_type))
      Dir.mkdir(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/qr", move_type))
    end
  end

  def check_stamper_status
    response = 500
    uri = URI(Rails.application.config.issuing['stamper_uri'])
    use_ssl = true
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = use_ssl
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    http.start do
      req = Net::HTTP::Get.new("/")
      response = http.request(req)
      return response.code
    end
  rescue
    return 500
  end

  def create_invoice_qr(t, file_name, binary_qr, move_type)
    File.open(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/qr/#{move_type}", file_name), 'wb') do |file|
      binary_data = Base64.decode64(binary_qr)
      file << binary_data
    end
  end

  def create_xml_stamped(t, file_name, stamped_xml, move_type)
    File.open(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/xml/#{move_type}", file_name), 'w') do |file|
      file << stamped_xml << "\n"
    end
  end

  def get_attributes(created_at, amount, total, folio, payment_method_key, postal_code, serie, comprobante, pay_method)
    attributes = {
      "Version" => Rails.application.config.issuing['Version'].to_s,
      "Serie" => serie,
      "Folio" => folio,
      "Fecha" => created_at,
      "FormaPago" => payment_method_key,
      "SubTotal" => amount,
      "Moneda" => "MXN",
      "Total" => total,
      "TipoDeComprobante" => comprobante,
      "MetodoPago" => pay_method,
      "LugarExpedicion" => postal_code.to_s
    }
  end

  def get_namespaces(type)
    if type == "invoice" # factura
      namespaces = {
        "xmlns:tfd" => "http://www.sat.gob.mx/TimbreFiscalDigital",
        "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
        "xsi:schemaLocation" => "http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd"
      }
    elsif type == "complement" # complemento de pago
      namespaces = {
        "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
        "xsi:schemaLocation" => "http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd http://www.sat.gob.mx/Pagos http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd"
      }
    end
  end

  def remove_invoice_files(uuid, file_id, url)
    unless url.blank?
      if File.exist?(Rails.root.join(url, "pdf", "#{uuid}_#{file_id}.pdf"))
        File.delete(Rails.root.join(url, "pdf", "#{uuid}_#{file_id}.pdf"))
      end
      if File.exist?(Rails.root.join(url, "xml", "#{uuid}_#{file_id}.xml"))
        File.delete(Rails.root.join(url, "xml", "#{uuid}_#{file_id}.xml"))
      end
    end
  end

  def remove_xml_origin(save_path)
    if File.exist?(save_path)
      File.delete(save_path)
    end
  end

  def send_invoice(mail_attributes)
    mail_response = InvoiceMailer.invoice_email_in(mail_attributes["name"], mail_attributes["email"], mail_attributes["attachments_name"], mail_attributes["attachments_url"]).deliver_now!
    if mail_response.status.to_i == 250
      return " Factura electrónica enviada exitosamente"
    else
      return " Error al enviar la factura electrónica"
    end
  rescue
    return " [Revisar configuración de servicio de email]"
  end
  ## /////// invoice methods ///////

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:userid, :first_name, :last_name, :email, :password, :remember_me) }
  end

  def respond_modal_with(*args, &blk)
    options = args.extract_options!
    options[:responder] = ModalResponder
    respond_with(*args, options, &blk)
  end
end
