require 'shopify_api'

# Magento attributes that must exist:
# vendor (brand)
# fulfillment_service
# barcode
# compare_at_price
# option1,2,3.....


module Gemgento::Adapter::Shopify
  class Product

    # Import products from Shopify to Gemgento.
    #
    # @param skip_existing [Boolean]
    # @return [void]
    def self.import(skip_existing = false)
      page = 1
      ShopifyAPI::Base.site = Gemgento::Adapter::ShopifyAdapter.api_url
      shopify_products = ShopifyAPI::Product.where(limit: 250, page: page)

      while shopify_products.any?

        shopify_products.each do |product|
          simple_products = []

          if product.variants.count > 1 && !skip?(skip_existing, product)
            product.variants.each do |variant|
              if skip?(skip_existing, variant)
                simple_products << Gemgento::Adapter::ShopifyAdapter.find_by_shopify_model(variant).gemgento_model
              else
                simple_products << create_simple_product(product, variant, false)
              end
            end

            create_configurable_product(product, simple_products)
          else
            create_simple_product(product, product.variants.first, true) unless skip?(skip_existing, product.variants.first)
          end
        end

        page = page + 1
        shopify_products = ShopifyAPI::Product.where(limit: 250, page: page)
      end

      push_tags
    end

    # Determine if product import can be skipped.
    #
    # @param skip_existing [Boolean]
    # @param shopify_model [Object]
    # @return [Boolean]
    def self.skip?(skip_existing, shopify_model)
      shopify_adapter = Gemgento::Adapter::ShopifyAdapter.find_by_shopify_model(shopify_model)
      return skip_existing && (shopify_adapter && !shopify_adapter.gemgento_model.magento_id.nil?)
    end

    # Create a simple product from Shopify product data.
    #
    # @param base_product [ShopifyAPI::Product]
    # @param variant [ShopifyAPI::Variant]
    # @param is_catalog_visible [Boolean]
    # @return [Gemgento::Product]
    def self.create_simple_product(base_product, variant, is_catalog_visible)
      product = initialize_product(variant, base_product, variant.sku, 'simple', is_catalog_visible, !base_product.published_at.nil?)

      product.set_attribute_value('barcode', variant.barcode)
      product.set_attribute_value('compare_at_price', variant.compare_at_price)
      product.set_attribute_value('fulfillment_service', variant.fulfillment_service)
      product.set_attribute_value('weight', variant.grams)
      product.set_attribute_value('price', variant.price)      
      #product.set_attribute_value('size', variant.size)

      if base_product.published_at.blank?
        product.set_attribute_value('status', 'enabled')
      else
        product.set_attribute_value('status', 'disabled')
      end
      if is_catalog_visible # catalog visible simple products are not part of a configurable
        product.set_attribute_value('name', base_product.title)
      else
        product.set_attribute_value('name', "#{base_product.title} - #{variant.title}")
      end

      product = set_option_values(product, base_product, variant)
      #product = set_categories(product, base_product.id)

      product.sync_needed = true
      product.save

      Gemgento::Adapter::ShopifyAdapter.create_association(product, variant) if product.shopify_adapter.nil?

      # begin
      #   product = create_assets(product, base_product.image, base_product.images)
      # rescue => e
      #   Rails.logger.warn "Unable to create assets, will ignore: #{e}"
      # end

      # create_tags(product, base_product.tags)
      create_inventory(product, variant)

      return product
    end

    # Create a simple product from Shopify product data.
    #
    # @param base_product [ShopifyAPI::Product]
    # @param simple_products [Array(Gemgento::Product)]
    # @return [Gemgento::Product]
    def self.create_configurable_product(base_product, simple_products)
      product = initialize_product(base_product, base_product, "#{simple_products.first.sku}_base", 'configurable', true, !base_product.published_at.nil?)

      product.set_attribute_value('barcode', simple_products.first.barcode)
      product.set_attribute_value('compare_at_price', simple_products.first.compare_at_price)
      product.set_attribute_value('fulfillment_service', simple_products.first.fulfillment_service)      
      product.set_attribute_value('weight', simple_products.first.weight)
      product.set_attribute_value('price', simple_products.first.price)
      product.set_attribute_value('name', base_product.title)
      if base_product.published_at.blank?
        product.set_attribute_value('status', 'enabled')
      else
        product.set_attribute_value('status', 'disabled')
      end

      product.sync_needed = false
      product.save

      product = set_configurable_attributes(product, base_product, base_product.variants.first)
      #product = set_categories(product, base_product.id)

      simple_products.uniq.each do |simple_product|
        simple_product.configurable_products << product unless simple_product.configurable_products.include? product
        simple_product.sync_needed
        simple_product.save
      end

      product.sync_needed = true
      product.save

      Gemgento::Adapter::ShopifyAdapter.create_association(product, base_product) if product.shopify_adapter.nil?

      # begin
      #   product = create_assets(product, base_product.image, base_product.images)
      # rescue => e
      #   Rails.logger.warn "Unable to create assets, will ignore: #{e}"
      # end

      # create_tags(product, base_product.tags)

      return product
    end

    # Initialize a Gemgento::Product given some basic data form Shopify product.
    #
    # @param shopify_model [Object] the shopify model that the product will be associated with through Gemgento::Adapter::ShopifyAdapter
    # @param base_product [ShopifyAPI::Product]
    # @param sku [String]
    # @param magento_type [String]
    # @param is_catalog_visible [Boolean]
    # @param status [Boolean]
    # @return [Gemgento::Product]
    def self.initialize_product(shopify_model, base_product, sku, magento_type, is_catalog_visible, status)
      if shopify_adapter = Gemgento::Adapter::ShopifyAdapter.find_by_shopify_model(shopify_model)
        product = shopify_adapter.gemgento_model
      else
        product = Gemgento::Product.not_deleted.find_or_initialize_by(sku: normalize_sku(sku, base_product, base_product.variants.first))
      end

      product.magento_type = magento_type
      product.visibility = is_catalog_visible ? 4 : 1
      product.product_attribute_set = Gemgento::ProductAttributeSet.first
      product.status = status
      product.sync_needed = false
      product.save

      product.set_attribute_value('url_key', base_product.handle)
      product.set_attribute_value('name', base_product.title)
      product.set_attribute_value('description', base_product.body_html)
      product.set_attribute_value('vendor', base_product.vendor)
      product.set_attribute_value('udropship_vendor', base_product.vendor)
      product.set_attribute_value('meta-keywords', base_product.tags)
      product.stores = Gemgento::Store.all

      puts 'PRODUCT ID: '+product.id.to_s+ '  DESCRIPTION: '+product.description

      return product
    end

    # Set product attribute values that have options.
    #
    # @param product [Gemgento::Product]
    # @param base_product [ShopifyAPI::Product]
    # @param variant [ShopifyAPI::Variant]
    # @return [Gemgento::Product]
    def self.set_option_values(product, base_product, variant)
      variant.attributes.each do |key, value|
        if key.to_s.include? 'option'
          code = get_option_attribute_code(base_product, key)
          next if code.nil?
          Rails.logger.warn  'ATTRIBUTE CODE: '+code
          value = 'N/A' if value.blank?
          product.set_attribute_value(code, value)
          Rails.logger.warn  'ATTRIBUTE VALUE: '+value
        end
      end

      product.sync_needed = false
      product.save

      return product
    end

    # Get the Gemgento::Attribute.code relate to an Shopify option code.
    #
    # @param base_product [ShopifyAPI::Product]
    # @param option_code [String]
    def self.get_option_attribute_code(base_product, option_code)
      index = option_code.to_s.gsub('option', '').to_i - 1
      return nil if index >= base_product.options.length
      return base_product.options[index].name.downcase
    end

    # Associate product with categories based on Shopify collections.
    #
    # @param product [Gemgento::Product]
    # @param shopify_id [Integer]
    # @return [Gemgento::Product]
    def self.set_categories(product, shopify_id)
      collections = ShopifyAPI::CustomCollection.where(product_id: shopify_id)

      Gemgento::Adapter::ShopifyAdapter.where(shopify_model_type: collections.first.class, shopify_model_id: collections.map(&:id)).each do |shopify_adapter|
        category = shopify_adapter.gemgento_model

        Gemgento::Store.all.each do |store|
          product_category = Gemgento::ProductCategory.find_or_initialize_by(product: product, category: category, store: store)
          product_category.sync_needed = false
          product_category.save
        end
      end

      product.product_categories.reload

      return product
    end

    # Define the configurable attributes for a configurable product.
    #
    # @param product [Gemgento::Product]
    # @param base_product [ShopifyAPI::Product]
    # @param variant [ShopifyAPI::Variant]
    # @return [Gemgento::Product]
    def self.set_configurable_attributes(product, base_product, variant)
      variant.attributes.each do |key, value|
        if key.to_s.include? 'option'
          code = get_option_attribute_code(base_product, key)
          next if code.nil?
          attribute = Gemgento::ProductAttribute.find_by(code: code)

          if attribute && attribute.is_configurable && is_configurable_attribute_used(product, attribute)
            product.configurable_attributes << attribute unless product.configurable_attributes.include? attribute
          end
        end
      end

      product.sync_needed = false
      product.save

      return product
    end

    # Check if a configurable attribute is actually being used to differentiate simples.
    #
    # @param product [Gemgento::Product]
    # @param attribute [Gemgento::ProductAttribute]
    # @return [Boolean]
    def self.is_configurable_attribute_used(product, attribute)
      product.simple_products.each do |simple_product|
        return false unless simple_product.product_attribute_values.exists?(product_attribute: attribute)
      end

      return true
    end

    # Create the assets for a product.
    #
    # @param product [Gemgento::Product]
    # @param base_image [ShopifyAPI::Image]
    # @param images [Array(ShopifyAPI::Image)]
    # @return [Gemgento::Product]
    def self.create_assets(product, base_image, images)
      images.each do |image|
        asset_file = nil

        product.stores.each do |store|
          asset = Gemgento::Asset.find_or_initialize_by(product: product, label: image.id)
          asset.asset_types << Gemgento::AssetType.find_by(code: 'image') if image == base_image
          asset.store = store
          asset.position = image.position
          asset.set_file(URI.parse(image.src))
          asset.sync_needed = false
          asset.save

          asset.sync_needed = true
          asset.save

          asset_file = asset.asset_file
        end

        Gemgento::Adapter::ShopifyAdapter.create_association(asset_file, image) unless asset_file.nil?
      end

      return product
    end

    # Create the inventory for the product.
    #
    # @param product [Gemgento::Product]
    # @param variant [ShopifyAPI::Variant]
    def self.create_inventory(product, variant)
      product.stores.each do |store|
        inventory = Gemgento::Inventory.find_or_initialize_by(product: product, store: store)
        inventory.quantity = variant.inventory_quantity
        inventory.is_in_stock = (inventory.quantity > 0 || variant.inventory_policy == 'continue')
        inventory.backorders = variant.inventory_policy == 'continue' ? 1 : 0
        inventory.sync_needed = inventory.new_record? || inventory.changed?
        inventory.save
      end
    end

    # Create and associate product with tags.
    #
    # @param product [Gemgento::Product]
    # @param tags [String]
    # @return [Void]
    def self.create_tags(product, tags)
      tags.split(',').each do |tag_name|
        tag = Gemgento::Tag.find_or_initialize_by(name: tag_name.strip)
        tag.status = 1
        tag.sync_needed = false
        tag.save

        Gemgento::Store.where.not(code: 'admin').each do |store|
          next if tag.stores.include? store
          
          tag.store_tags.create(store: store)
        end

        tag.products << product unless tag.products.include? product
        tag.sync_needed = false
        tag.save
      end
    end

    # Push all tags to Magento.
    #
    # @return [Void]
    def self.push_tags
      Gemgento::Tag.all.each do |tag|
        tag.sync_needed = true
        tag.save
      end
    end

    # Normalize the sku.
    #
    # @param sku [String]
    # @param base_product [ShopifyAPI::Product]
    # @param variant [ShopifyAPI::ProductVariant]
    # @return [String]
    def self.normalize_sku(sku, base_product, variant)
      return sku
    end

  end
end