import { StateCreator } from 'zustand';
import { __ } from '@wordpress/i18n';
import { uuid } from '../utilities';
import type { PricingLevel, Product } from '../typings/types';
import type { PricingRuleSlice, CombinedStore, ImportCounts } from './types';

const { gp_conditional_pricing_form_settings_strings: strings } = window;

interface PricingRulesResp {
	success: boolean;
	data: {};
}

export const createPricingRulesSlice: StateCreator<
	CombinedStore,
	[],
	[],
	PricingRuleSlice
> = (set, getState) => ({
	products: null,
	activeProductId: null,
	activePricingLevelId: null,
	hasUnsavedChanges: false,
	setProducts: (products: Product[]) => {
		set({
			products: products.map((product) => {
				if (Array.isArray(product.pricingLevels)) {
					product.pricingLevels = product.pricingLevels
						?.map((pricingLevel) => {
							if (!pricingLevel.uuid) {
								pricingLevel.uuid = uuid();
							}

							return pricingLevel;
						})
						.sort((a, b) => {
							if (a.position && b.position) {
								return a.position - b.position;
							}

							return 0;
						});
				}

				return product;
			}),
		});
	},
	getProductsWithPricingLevels: () => {
		const products = getState().products ?? [];
		return products.filter((product) =>
			Array.isArray(product.pricingLevels)
		);
	},
	initializeProductPricingLevels: (productId: string) => {
		/**
		 * create a new pricing level uuid that we can then use to set as the
		 * activePriceLevelId and on the newly created "dummy" pricing level
		 * that we will create below.
		 */
		const activePricingLevelId = uuid();

		const products = getState().products?.map((product) => {
			if (
				// product.id is either a string or a number, so we need to convert
				// it to a string in order to compare it to productId which will always
				// be a string.
				String(product.id) !== productId ||
				(Array.isArray(product.pricingLevels) &&
					product.pricingLevels.length > 0)
			) {
				return product;
			}

			return {
				...product,
				// initialize a defalt pricing level
				pricingLevels: [
					{
						uuid: activePricingLevelId,
						price: '0.00',
						productId: product.id,
						conditionalLogic: new window.ConditionalLogic(),
					},
				],
			} as Product;
		});

		set({
			products,
			activeProductId: productId,
			activePricingLevelId,
		});
	},
	addProductPricingLevel: (productId: number) => {
		/**
		 * create a new pricing level uuid that we can then use to set as the
		 * activePriceLevelId and on the newly created "dummy" pricing level
		 * that we will create below.
		 */
		const activePricingLevelId = uuid();

		const products = getState().products?.map((product) => {
			if (product.id === productId) {
				if (!Array.isArray(product.pricingLevels)) {
					product.pricingLevels = [];
				}

				product.pricingLevels.push({
					uuid: activePricingLevelId,
					price: '0.00',
					productId: product.id,
					conditionalLogic: new window.ConditionalLogic(),
				} as PricingLevel);
			}

			return product;
		});

		set({
			products,
			activeProductId: String(productId),
			activePricingLevelId,
			hasUnsavedChanges: true,
		});
	},
	setActiveProductId: (id: number | null) =>
		set({ activeProductId: String(id) }),
	setActivePricingLevel: (id: string | null) => {
		set({ activePricingLevelId: id });
	},
	getActivePricingLevel: () => {
		const state = getState();

		const activeProduct = state.products?.find(
			(product) => String(product.id) === state.activeProductId
		);
		if (!activeProduct) {
			return null;
		}

		return (
			activeProduct.pricingLevels.find(
				(pricingLevel) =>
					pricingLevel.uuid === state.activePricingLevelId
			) ?? null
		);
	},
	updateActiveProductPricingLevel: (pricingLevel: PricingLevel) => {
		const activeProductId = getState().activeProductId;

		const products = getState().products?.map((product) => {
			if (String(product.id) !== activeProductId) {
				return product;
			}

			return {
				...product,
				pricingLevels: product.pricingLevels.map((currPricingLevel) => {
					if (currPricingLevel.uuid !== pricingLevel.uuid) {
						return currPricingLevel;
					}

					// TODO diff the pricing level and only set hasUnsavedChanges if there are changes.

					return pricingLevel;
				}),
			};
		});

		set({
			products,
			hasUnsavedChanges: true,
		});
	},
	deleteActiveProductPricingLevel: (pricingLevelId: string) => {
		const activeProductId = getState().activeProductId;

		const products = getState().products?.map((product) => {
			if (String(product.id) !== activeProductId) {
				return product;
			}

			return {
				...product,
				pricingLevels: product.pricingLevels.filter(
					(pricingLevel) => pricingLevel.uuid !== pricingLevelId
				),
			};
		});

		set({
			products,
			hasUnsavedChanges: true,
		});
	},
	setHasUnsavedChanges: (hasUnsavedChanges: boolean) =>
		set({ hasUnsavedChanges }),
	setPricingLevelOrder: (
		productId: number,
		pricingLevels: PricingLevel[]
	) => {
		const products = getState().products?.map((product) => {
			if (product.id !== productId) {
				return product;
			}

			return {
				...product,
				pricingLevels: pricingLevels.map((pricingLevel, i) => ({
					...pricingLevel,
					position: i,
				})),
			};
		});

		set({
			products,
			hasUnsavedChanges: true,
		});
	},
	importCounts: null,
	setImportCounts: (counts: ImportCounts | null) =>
		set({ importCounts: counts }),
	savePricingLogic: async (products: Product[]) => {
		const { setHasUnsavedChanges, setImportCounts, addBanner } = getState();
		try {
			const resp: PricingRulesResp = await jQuery.ajax({
				// We send this request as a JSON body which is why we have to use a query parameter for the action.
				url: strings.ajax_url + '?action=gwcp_save_condtional_logic',
				contentType: 'application/json',
				dataType: 'json',
				data: JSON.stringify({
					_ajax_nonce: strings.nonce,
					form_id: strings.form_id,
					products,
				}),
				type: 'POST',
			});

			if (!resp.success) {
				throw new Error('Error saving pricing rules');
			}

			setHasUnsavedChanges(false);
			setImportCounts(null);
		} catch (err) {
			addBanner(
				__('Error saving pricing rules.', 'gp-conditional-pricing'),
				'error'
			);
			// eslint-disable-next-line no-console
			console.error(err);
		} finally {
		}
	},
});
