package com.shopify.model;

import java.math.BigDecimal;
import java.math.RoundingMode;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

public class ShopifyVariantUpdateRequest implements ShopifyVariantRequest {

	private final ShopifyVariant request;
	private final String imageSource;
	private final boolean changed;

	public static interface CurrentShopifyVariantStep {
		public PriceStep withCurrentShopifyVariant(final ShopifyVariant shopifyVariant);
	}

	public static interface PriceStep {
		public CompareAtPriceStep withPrice(final BigDecimal price);

		public CompareAtPriceStep withSamePrice();
	}

	public static interface CompareAtPriceStep {
		public SkuStep withCompareAtPrice(final BigDecimal compareAtPrice);

		public SkuStep withSameCompareAtPrice();
	}

	public static interface SkuStep {
		public BarcodeStep withSku(final String sku);

		public BarcodeStep withSameSku();
	}

	public static interface BarcodeStep {
		public WeightStep withBarcode(final String barcode);

		public WeightStep withSameBarcode();
	}

	public static interface WeightStep {
		public AvailableStep withWeight(final BigDecimal weight);

		public AvailableStep withSameWeight();
	}

	public static interface AvailableStep {
		public FirstOptionStep withAvailable(final long quantity);
	}

	public static interface FirstOptionStep {
		public SecondOptionStep withFirstOption(final String option);

		public SecondOptionStep withSameFirstOption();

		public SecondOptionStep noFirstOption();
	}

	public static interface SecondOptionStep {
		public ThirdOptionStep withSecondOption(final String option);

		public ThirdOptionStep withSameSecondOption();

		public ThirdOptionStep noSecondOption();
	}

	public static interface ThirdOptionStep {
		public ImageSourceStep withThirdOption(final String option);

		public ImageSourceStep withSameThirdOption();

		public ImageSourceStep noThirdOption();
	}

	public static interface ImageSourceStep {
		public InventoryManagementStep withImageSource(final String imageSource);

		public InventoryManagementStep withSameImage();

		public InventoryManagementStep noImage();
	}

	public static interface InventoryManagementStep {
		public InventoryPolicyStep withInventoryManagement(final String inventoryManagement);

		public InventoryPolicyStep withSameInventoryManagement();
	}

	public static interface InventoryPolicyStep {
		public FulfillmentServiceStep withInventoryPolicy(final InventoryPolicy inventoryPolicy);

		public FulfillmentServiceStep withSameInventoryPolicy();
	}

	public static interface FulfillmentServiceStep {
		public RequiresShippingStep withFulfillmentService(final String fulfillmentService);

		public RequiresShippingStep withSameFulfillmentService();
	}

	public static interface RequiresShippingStep {
		public TaxableStep withRequiresShipping(final boolean requiresShipping);

		public TaxableStep withSameRequiresShipping();
	}

	public static interface TaxableStep {
		public InventoryItemIdStep withTaxable(final boolean taxable);

		public InventoryItemIdStep withSameTaxable();
	}

	public static interface InventoryItemIdStep {
		public BuildStep withInventoryItemId(final String inventoryItemId);

		public BuildStep withSameInventoryItemId();
	}

	public static interface BuildStep {
		public ShopifyVariantUpdateRequest build();
	}

	public static CurrentShopifyVariantStep newBuilder() {
		return new Steps();
	}

	@Override
	public ShopifyVariant getRequest() {
		return request;
	}

	@Override
	public String getImageSource() {
		return imageSource;
	}

	@Override
	public boolean hasImageSource() {
		return imageSource != null;
	}

	@Override
	public boolean hasChanged() {
		return changed;
	}

	private ShopifyVariantUpdateRequest(final ShopifyVariant shopifyVariant, final String imageSource,
			final boolean changed) {
		this.request = shopifyVariant;
		this.imageSource = imageSource;
		this.changed = changed;
	}

	private static class Steps implements CurrentShopifyVariantStep, PriceStep, CompareAtPriceStep, SkuStep,
			BarcodeStep, WeightStep, AvailableStep, FirstOptionStep, SecondOptionStep, ThirdOptionStep, ImageSourceStep,
			InventoryManagementStep, InventoryPolicyStep, FulfillmentServiceStep, RequiresShippingStep, TaxableStep,
			InventoryItemIdStep, BuildStep {
		private static final int ZERO = 0;

		private ShopifyVariant shopifyVariant;
		private String imageSource;
		private boolean changed;

		@Override
		public ShopifyVariantUpdateRequest build() {
			return new ShopifyVariantUpdateRequest(shopifyVariant, imageSource, changed);
		}

		@Override
		public ImageSourceStep withThirdOption(final String option) {
			if (doesNotEqual(shopifyVariant.getOption3(), option)) {
				shopifyVariant.setOption3(option);
				changed = true;
			}
			return this;
		}

		@Override
		public ImageSourceStep noThirdOption() {
			if (isNotNull(shopifyVariant.getOption3())) {
				shopifyVariant.setOption3(null);
				changed = true;
			}
			return this;
		}

		@Override
		public ThirdOptionStep withSecondOption(final String option) {
			if (doesNotEqual(shopifyVariant.getOption2(), option)) {
				shopifyVariant.setOption2(option);
				changed = true;
			}
			return this;
		}

		@Override
		public ThirdOptionStep noSecondOption() {
			if (isNotNull(shopifyVariant.getOption2())) {
				shopifyVariant.setOption2(null);
				changed = true;
			}
			return this;
		}

		@Override
		public SecondOptionStep withFirstOption(final String option) {
			if (doesNotEqual(shopifyVariant.getOption1(), option)) {
				shopifyVariant.setOption1(option);
				changed = true;
			}
			return this;
		}

		@Override
		public SecondOptionStep noFirstOption() {
			if (isNotNull(shopifyVariant.getOption1())) {
				shopifyVariant.setOption1(null);
				changed = true;
			}
			return this;
		}

		@Override
		public FirstOptionStep withAvailable(final long available) {
			shopifyVariant.setAvailable(available);
			return this;
		}

		@Override
		public AvailableStep withWeight(final BigDecimal weight) {
			final long grams = weight.setScale(ZERO, RoundingMode.HALF_UP).longValueExact();
			if (doesNotEqual(shopifyVariant.getGrams(), grams)) {
				shopifyVariant.setGrams(grams);
				changed = true;
			}
			return this;
		}

		@Override
		public WeightStep withBarcode(final String barcode) {
			if (doesNotEqual(shopifyVariant.getBarcode(), barcode)) {
				shopifyVariant.setBarcode(barcode);
				changed = true;
			}
			return this;
		}

		@Override
		public BarcodeStep withSku(final String sku) {
			if (doesNotEqual(shopifyVariant.getSku(), sku)) {
				shopifyVariant.setSku(sku);
				changed = true;
			}
			return this;
		}

		@Override
		public CompareAtPriceStep withPrice(final BigDecimal money) {
			if (shopifyVariant.getPrice() == null || ZERO != money.compareTo(shopifyVariant.getPrice())) {
				shopifyVariant.setPrice(money);
				changed = true;
			}
			return this;
		}

		@Override
		public SkuStep withCompareAtPrice(final BigDecimal money) {
			if (shopifyVariant.getCompareAtPrice() == null
					|| ZERO != money.compareTo(shopifyVariant.getCompareAtPrice())) {
				shopifyVariant.setCompareAtPrice(money);
				changed = true;
			}
			return this;
		}

		@Override
		public InventoryManagementStep withImageSource(final String imageSource) {
			this.imageSource = imageSource;
			shopifyVariant.setImageId(null);
			return this;
		}

		@Override
		public InventoryManagementStep noImage() {
			if (isNotNull(shopifyVariant.getImageId())) {
				shopifyVariant.setImageId(null);
				changed = true;
			}
			return this;
		}

		@Override
		public InventoryItemIdStep withTaxable(final boolean taxable) {
			if (doesNotEqual(shopifyVariant.isTaxable(), taxable)) {
				shopifyVariant.setTaxable(taxable);
				changed = true;
			}
			return this;
		}

		@Override
		public TaxableStep withRequiresShipping(final boolean requiresShipping) {
			if (doesNotEqual(shopifyVariant.isRequiresShipping(), requiresShipping)) {
				shopifyVariant.setRequiresShipping(requiresShipping);
				changed = true;
			}
			return this;
		}

		@Override
		public RequiresShippingStep withFulfillmentService(final String fulfillmentService) {
			if (doesNotEqual(shopifyVariant.getFulfillmentService(), fulfillmentService)) {
				shopifyVariant.setFulfillmentService(fulfillmentService);
				changed = true;
			}
			return this;
		}

		@Override
		public FulfillmentServiceStep withInventoryPolicy(final InventoryPolicy inventoryPolicy) {
			if (shopifyVariant.getInventoryPolicy() != inventoryPolicy) {
				shopifyVariant.setInventoryPolicy(inventoryPolicy);
				changed = true;
			}
			return this;
		}

		@Override
		public InventoryPolicyStep withInventoryManagement(final String inventoryManagement) {
			if (doesNotEqual(shopifyVariant.getInventoryManagement(), inventoryManagement)) {
				shopifyVariant.setInventoryManagement(inventoryManagement);
				changed = true;
			}
			return this;
		}

		@Override
		public InventoryItemIdStep withSameTaxable() {
			return this;
		}

		@Override
		public TaxableStep withSameRequiresShipping() {
			return this;
		}

		@Override
		public RequiresShippingStep withSameFulfillmentService() {
			return this;
		}

		@Override
		public FulfillmentServiceStep withSameInventoryPolicy() {
			return this;
		}

		@Override
		public InventoryPolicyStep withSameInventoryManagement() {
			return this;
		}

		@Override
		public InventoryManagementStep withSameImage() {
			return this;
		}

		@Override
		public ImageSourceStep withSameThirdOption() {
			return this;
		}

		@Override
		public ThirdOptionStep withSameSecondOption() {
			return this;
		}

		@Override
		public SecondOptionStep withSameFirstOption() {
			return this;
		}

		@Override
		public AvailableStep withSameWeight() {
			return this;
		}

		@Override
		public WeightStep withSameBarcode() {
			return this;
		}

		@Override
		public BarcodeStep withSameSku() {
			return this;
		}

		@Override
		public CompareAtPriceStep withSamePrice() {
			return this;
		}

		@Override
		public SkuStep withSameCompareAtPrice() {
			return this;
		}

		@Override
		public PriceStep withCurrentShopifyVariant(final ShopifyVariant shopifyVariant) {
			this.shopifyVariant = shopifyVariant;
			return this;
		}

		private boolean doesNotEqual(final String s1, final String s2) {
			final String unescapedS1 = StringEscapeUtils.unescapeHtml4(StringUtils.trim(s1));
			final String unescapedS2 = StringEscapeUtils.unescapeHtml4(StringUtils.trim(s2));
			return !StringUtils.equals(unescapedS1, unescapedS2);
		}

		private boolean doesNotEqual(final long l1, final long l2) {
			return l1 != l2;
		}

		private boolean doesNotEqual(final boolean b1, final boolean b2) {
			return b1 != b2;
		}

		private boolean isNotNull(final Object object) {
			return object != null;
		}

		@Override
		public BuildStep withInventoryItemId(final String inventoryItemId) {
			this.shopifyVariant.setInventoryItemId(inventoryItemId);
			return this;
		}

		@Override
		public BuildStep withSameInventoryItemId() {
			return this;
		}

	}

}