/**
 * Inspector Controls
 */

// Setup the block
const { __ } = wp.i18n;
const { Component, Fragment } = wp.element;

import compact from 'lodash/compact';
import map from 'lodash/map';
import RenderSettingControl from '../../../utils/components/settings/renderSettingControl';

// Import block components
const { InspectorControls } = wp.blockEditor;

// Import Inspector components
const {
	PanelBody,
	QueryControls,
	RangeControl,
	SelectControl,
	TextControl,
	ToggleControl,
	FormTokenField,
	Spinner,
} = wp.components;

const { addQueryArgs } = wp.url;

const { apiFetch } = wp;

const MAX_POSTS_COLUMNS = 4;

const inputDelay = [];

/**
 * Create an Inspector Controls wrapper Component
 */
export default class Inspector extends Component {
	constructor() {
		super(...arguments);
		this.state = {
			categoriesList: false,
			categoriesTitleToIdRelationships: false,
			categoriesIdToTitleRelationships: false,
			pagesList: false,
			pagesTitleToIdRelationships: false,
			pagesIdToTitleRelationships: false,
			waitingForApiResponse: false,
		};
	}

	componentDidMount() {
		this.stillMounted = true;
	}

	componentDidUpdate() {
		// If this block was just selected by the user, fetch the previously-selected category slugs from the server so we have something to show in the sidebar.
		if (
			this.props.isSelected &&
			!this.state.waitingForApiResponse &&
			this.props.attributes.categories &&
			!this.state.categoriesList &&
			'post' === this.props.attributes.postType
		) {
			this.getCategoriesFromServer(
				this.props.attributes.categories
					? this.props.attributes.categories
					: false,
				true
			);
		}

		// If this block was just selected by the user, fetch the previously-selected page slugs from the server so we have something to show in the sidebar.
		if (
			this.props.isSelected &&
			!this.state.waitingForApiResponse &&
			this.props.attributes.selectedPages.length > 0 &&
			!this.state.pagesList &&
			'page' === this.props.attributes.postType
		) {
			const ids = [];
			for (const selectedPage in this.props.attributes.selectedPages) {
				ids.push(
					this.props.attributes.selectedPages[selectedPage].value
				);
			}

			this.getPagesFromServer(ids ? ids : false, true);
		}
	}

	componentWillUnmount() {
		this.stillMounted = false;
	}

	getCategoriesFromServer(userInput, isInitial = false) {
		return new Promise((resolve) => {
			this.setState({
				waitingForApiResponse: true,
				categoriesList: false,
			});

			if (!userInput || userInput.length === 0) {
				if (!isInitial) {
					this.setState({
						waitingForApiResponse: false,
					});
				}
				resolve();
				return;
			}

			let args = {
				per_page: 99,
				search: userInput,
			};

			if (isInitial) {
				args = {
					per_page: 99,
					include: userInput,
				};
			}

			this.fetchRequest = apiFetch({
				path: addQueryArgs('/wp/v2/categories', args),
			})
				.then((categoriesList) => {
					// Store arrays for slug-to-id and id-to-slug to make them easy to reference in components and attributes.
					const categoriesTitleToIdRelationships = this.state
						.categoriesTitleToIdRelationships
						? this.state.categoriesTitleToIdRelationships
						: {};
					const categoriesIdToTitleRelationships = this.state
						.categoriesIdToTitleRelationships
						? this.state.categoriesIdToTitleRelationships
						: {};

					for (const category in categoriesList) {
						categoriesTitleToIdRelationships[
							categoriesList[category].name +
								' (' +
								categoriesList[category].slug +
								')'
						] = categoriesList[category].id;
						categoriesIdToTitleRelationships[
							categoriesList[category].id
						] =
							categoriesList[category].name +
							' (' +
							categoriesList[category].slug +
							')';
					}

					this.setState({
						categoriesList,
						categoriesTitleToIdRelationships,
						categoriesIdToTitleRelationships,
						waitingForApiResponse: false,
					});

					resolve();
				})
				.catch(() => {
					console.log(`category request failure: ${error.message}`);
					if (this.stillMounted) {
						this.setState({
							categoriesList: [],
							waitingForApiResponse: false,
						});

						resolve();
					}
				});
		});
	}

	getPagesFromServer(userInput, isInitial = false) {
		return new Promise((resolve) => {
			this.setState({
				waitingForApiResponse: true,
				pagesList: false,
			});

			if (!userInput || userInput.length === 0) {
				if (!isInitial) {
					this.setState({
						waitingForApiResponse: false,
					});
				}
				resolve();
				return;
			}

			let args = {
				per_page: -1,
				search: userInput,
			};

			if (isInitial) {
				args = {
					per_page: -1,
					include: userInput,
				};
			}

			this.fetchRequest = apiFetch({
				path: addQueryArgs('/wp/v2/pages', args),
			})
				.then((pagesList) => {
					// Store arrays for slug-to-id and id-to-slug to make them easy to reference in components and attributes.
					const pagesTitleToIdRelationships = this.state
						.pagesTitleToIdRelationships
						? this.state.pagesTitleToIdRelationships
						: {};
					const pagesIdToTitleRelationships = this.state
						.pagesIdToTitleRelationships
						? this.state.pagesIdToTitleRelationships
						: {};

					for (const page in pagesList) {
						pagesTitleToIdRelationships[
							pagesList[page].title.rendered +
								' (' +
								pagesList[page].slug +
								')'
						] = pagesList[page].id;
						pagesIdToTitleRelationships[pagesList[page].id] =
							pagesList[page].title.rendered +
							' (' +
							pagesList[page].slug +
							')';
					}

					this.setState({
						pagesList,
						pagesTitleToIdRelationships,
						pagesIdToTitleRelationships,
						waitingForApiResponse: false,
					});

					resolve();
				})
				.catch(() => {
					if (this.stillMounted) {
						this.setState({
							pagesList: [],
							waitingForApiResponse: false,
						});
						resolve();
					}
				});
		});
	}

	/* Get the available image sizes */
	imageSizeSelect() {
		const getSettings = wp.data.select('core/block-editor').getSettings();

		return compact(
			map(getSettings.imageSizes, ({ name, slug }) => {
				return {
					value: slug,
					label: name,
				};
			})
		);
	}

	render() {
		// Setup the attributes
		const { attributes, setAttributes, latestPosts } = this.props;

		const { order, orderBy } = attributes;

		const {
			categoriesList,
			categoriesTitleToIdRelationships,
			categoriesIdToTitleRelationships,
			pagesList,
			pagesTitleToIdRelationships,
			pagesIdToTitleRelationships,
		} = this.state;

		// Post type options
		const postTypeOptions = [
			{ value: 'post', label: __('Post', 'genesis-blocks') },
			{ value: 'page', label: __('Page', 'genesis-blocks') },
		];

		// Section title tags
		const sectionTags = [
			{ value: 'div', label: __('div', 'genesis-blocks') },
			{ value: 'header', label: __('header', 'genesis-blocks') },
			{ value: 'section', label: __('section', 'genesis-blocks') },
			{ value: 'article', label: __('article', 'genesis-blocks') },
			{ value: 'main', label: __('main', 'genesis-blocks') },
			{ value: 'aside', label: __('aside', 'genesis-blocks') },
			{ value: 'footer', label: __('footer', 'genesis-blocks') },
		];

		// Section title tags
		const sectionTitleTags = [
			{ value: 'h2', label: __('H2', 'genesis-blocks') },
			{ value: 'h3', label: __('H3', 'genesis-blocks') },
			{ value: 'h4', label: __('H4', 'genesis-blocks') },
			{ value: 'h5', label: __('H5', 'genesis-blocks') },
			{ value: 'h6', label: __('H6', 'genesis-blocks') },
		];

		// Check for posts
		const hasPosts = Array.isArray(latestPosts) && latestPosts.length;

		// Check the post type
		const isPost = 'post' === attributes.postType;

		// Add instruction text to the select
		const abImageSizeSelect = {
			value: 'selectimage',
			label: __('Select image size', 'genesis-blocks'),
		};

		// Add the landscape image size to the select
		const abImageSizeLandscape = {
			value: 'gb-block-post-grid-landscape',
			label: __('GB Grid Landscape', 'genesis-blocks'),
		};

		// Add the square image size to the select
		const abImageSizeSquare = {
			value: 'gb-block-post-grid-square',
			label: __('GB Grid Square', 'genesis-blocks'),
		};

		// Get the image size options
		const imageSizeOptions = this.imageSizeSelect();

		// Combine the objects
		imageSizeOptions.push(abImageSizeSquare, abImageSizeLandscape);
		imageSizeOptions.unshift(abImageSizeSelect);

		const imageSizeValue = () => {
			for (let i = 0; i < imageSizeOptions.length; i++) {
				if (imageSizeOptions[i].value === attributes.imageSize) {
					return attributes.imageSize;
				}
			}
			return 'full';
		};

		return (
			<InspectorControls>
				<PanelBody
					title={__('Post and Page Grid Settings', 'genesis-blocks')}
					className={isPost ? null : 'genesis-blocks-hide-query'}
				>
					<RenderSettingControl id="gb_postgrid_postType">
						<SelectControl
							label={__('Content Type', 'genesis-blocks')}
							options={postTypeOptions}
							value={attributes.postType}
							onChange={(value) =>
								this.props.setAttributes({ postType: value })
							}
						/>
					</RenderSettingControl>
					{'page' === attributes.postType && (
						<RenderSettingControl id="gb_postgrid_selectedPages">
							<div className="components-base-control">
								<div
									className="components-base-control__field"
									style={{ position: 'relative' }}
								>
									<FormTokenField
										suggestions={compact(
											map(
												pagesList,
												({ title, slug }) => {
													return (
														title.rendered +
														' (' +
														slug +
														')'
													);
												}
											)
										)}
										label={
											<>
												{__(
													'Enter page names to display',
													'genesis-blocks'
												)}
												{this.state
													.waitingForApiResponse ? (
													<div
														style={{
															position:
																'absolute',
															bottom: '30px',
															right: '0px',
														}}
													>
														<Spinner />
													</div>
												) : null}
											</>
										}
										placeholder={__(
											'Start typing page name…',
											'genesis-blocks'
										)}
										value={(() => {
											if (
												!this.props.attributes
													.selectedPages
											) {
												return [];
											}

											const values = [];

											for (const selectedPage in this
												.props.attributes
												.selectedPages) {
												const pageId =
													this.props.attributes
														.selectedPages[
														selectedPage
													].value;

												if (
													pagesIdToTitleRelationships[
														pageId
													]
												) {
													values.push(
														pagesIdToTitleRelationships[
															this.props
																.attributes
																.selectedPages[
																selectedPage
															].value
														]
													);
												}
											}

											return values;
										})()}
										onInputChange={(userInput) => {
											const delayName =
												'getPagesFromServer';

											// Set up a delay which waits to search the api until the user takes a .5 second break from typing.
											if (inputDelay[delayName]) {
												// Clear the keypress delay if the user just typed
												clearTimeout(
													inputDelay[delayName]
												);
												inputDelay[delayName] = null;
											}

											// (Re)-Set up the save to fire in 500ms
											inputDelay[delayName] = setTimeout(
												() => {
													clearTimeout(
														inputDelay[delayName]
													);

													// When the user types in the field, search the API for matching categories.
													this.getPagesFromServer(
														userInput
													);
												},
												500
											);
										}}
										onChange={(newPagesSlugs) => {
											let selectedPages = [];

											// Loop through each category slug chosen by the user, and populate the selectedPages attribute.
											for (const page in newPagesSlugs) {
												selectedPages.push({
													value: pagesTitleToIdRelationships[
														newPagesSlugs[page]
													],
												});
											}

											if (!selectedPages) {
												selectedPages = undefined;
											}

											setAttributes({ selectedPages });
										}}
									/>
								</div>
							</div>
						</RenderSettingControl>
					)}

					{'post' === attributes.postType && (
						<RenderSettingControl id="gb_postgrid_categories">
							<div className="components-base-control">
								<div
									className="components-base-control__field"
									style={{ position: 'relative' }}
								>
									<FormTokenField
										suggestions={compact(
											map(
												categoriesList,
												({ name, slug }) => {
													return (
														name + ' (' + slug + ')'
													);
												}
											)
										)}
										label={
											<>
												{__(
													'Enter category names to display',
													'genesis-blocks'
												)}
												{this.state
													.waitingForApiResponse ? (
													<div
														style={{
															position:
																'absolute',
															bottom: '30px',
															right: '0px',
														}}
													>
														<Spinner />
													</div>
												) : null}
											</>
										}
										placeholder={__(
											'Start typing category name…',
											'genesis-blocks'
										)}
										value={(() => {
											if (
												!this.props.attributes
													.categories
											) {
												return [];
											}

											// Convert the string of category IDs to an array.
											const categoryIdArray =
												this.props.attributes.categories.split(
													','
												);

											const values = [];

											// Convert each ID to its slug.
											for (const categoryId in categoryIdArray) {
												if (
													categoriesIdToTitleRelationships[
														categoryIdArray[
															categoryId
														]
													]
												) {
													values.push(
														categoriesIdToTitleRelationships[
															categoryIdArray[
																categoryId
															]
														]
													);
												}
											}

											return values;
										})()}
										onInputChange={(userInput) => {
											const delayName =
												'getCategoriesFromServer';

											// Set up a delay which waits to search the api until the user takes a .5 second break from typing.
											if (inputDelay[delayName]) {
												// Clear the keypress delay if the user just typed
												clearTimeout(
													inputDelay[delayName]
												);
												inputDelay[delayName] = null;
											}

											// (Re)-Set up the save to fire in 500ms
											inputDelay[delayName] = setTimeout(
												() => {
													clearTimeout(
														inputDelay[delayName]
													);

													// When the user types in the field, search the API for matching categories.
													this.getCategoriesFromServer(
														userInput
													);
												},
												500
											);
										}}
										onChange={(newCategorySlugs) => {
											let chosenCatIdString = '';

											// Loop through each category slug chosen by the user, and build a comma-separated string with each corresponding ID.
											for (const category in newCategorySlugs) {
												if (
													categoriesTitleToIdRelationships[
														newCategorySlugs[
															category
														]
													]
												) {
													chosenCatIdString =
														chosenCatIdString +
														categoriesTitleToIdRelationships[
															newCategorySlugs[
																category
															]
														] +
														',';
												}
											}

											//Remove trailing comma and whitespace.
											chosenCatIdString =
												chosenCatIdString.replace(
													/,\s*$/,
													''
												);

											if (!chosenCatIdString) {
												chosenCatIdString = undefined;
											}

											// Note that we parse the category id to be a string, as the attribute was originally defined as a string for this block.
											setAttributes({
												categories:
													undefined !==
													chosenCatIdString
														? chosenCatIdString
														: '',
											});
										}}
									/>
								</div>
							</div>
						</RenderSettingControl>
					)}

					{'post' === attributes.postType && (
						<>
							<RenderSettingControl id="gb_postgrid_queryControls">
								<QueryControls
									{...{ order, orderBy }}
									numberOfItems={attributes.postsToShow}
									onOrderChange={(value) =>
										setAttributes({ order: value })
									}
									onOrderByChange={(value) =>
										setAttributes({ orderBy: value })
									}
									onNumberOfItemsChange={(value) =>
										setAttributes({ postsToShow: value })
									}
								/>
							</RenderSettingControl>
							<RenderSettingControl id="gb_postgrid_offset">
								<RangeControl
									label={__(
										'Number of items to offset',
										'genesis-blocks'
									)}
									value={attributes.offset}
									onChange={(value) =>
										setAttributes({ offset: value })
									}
									min={0}
									max={20}
								/>
							</RenderSettingControl>
						</>
					)}

					{'grid' === attributes.postLayout && (
						<RenderSettingControl id="gb_postgrid_columns">
							<RangeControl
								label={__('Columns', 'genesis-blocks')}
								value={attributes.columns}
								onChange={(value) =>
									setAttributes({ columns: value })
								}
								min={1}
								max={
									!hasPosts
										? MAX_POSTS_COLUMNS
										: Math.min(
												MAX_POSTS_COLUMNS,
												latestPosts.length
											)
								}
							/>
						</RenderSettingControl>
					)}
				</PanelBody>
				<PanelBody
					title={__('Post and Page Grid Content', 'genesis-blocks')}
					initialOpen={false}
				>
					<RenderSettingControl id="gb_postgrid_displaySectionTitle">
						<ToggleControl
							label={__(
								'Display Section Title',
								'genesis-blocks'
							)}
							checked={attributes.displaySectionTitle}
							onChange={() =>
								this.props.setAttributes({
									displaySectionTitle:
										!attributes.displaySectionTitle,
								})
							}
						/>
					</RenderSettingControl>
					{attributes.displaySectionTitle && (
						<RenderSettingControl id="gb_postgrid_sectionTitle">
							<TextControl
								label={__('Section Title', 'genesis-blocks')}
								type="text"
								value={attributes.sectionTitle}
								onChange={(value) =>
									this.props.setAttributes({
										sectionTitle: value,
									})
								}
							/>
						</RenderSettingControl>
					)}
					<RenderSettingControl id="gb_postgrid_displayPostImage">
						<ToggleControl
							label={__(
								'Display Featured Image',
								'genesis-blocks'
							)}
							checked={attributes.displayPostImage}
							onChange={() =>
								this.props.setAttributes({
									displayPostImage:
										!attributes.displayPostImage,
								})
							}
						/>
					</RenderSettingControl>
					{attributes.displayPostImage && (
						<RenderSettingControl id="gb_postgrid_imageSizeValue">
							<SelectControl
								label={__('Image Size', 'genesis-blocks')}
								value={imageSizeValue()}
								options={imageSizeOptions}
								onChange={(value) =>
									this.props.setAttributes({
										imageSize: value,
									})
								}
							/>
						</RenderSettingControl>
					)}
					<RenderSettingControl id="gb_postgrid_displayPostTitle">
						<ToggleControl
							label={__('Display Title', 'genesis-blocks')}
							checked={attributes.displayPostTitle}
							onChange={() =>
								this.props.setAttributes({
									displayPostTitle:
										!attributes.displayPostTitle,
								})
							}
						/>
					</RenderSettingControl>
					{isPost && (
						<RenderSettingControl id="gb_postgrid_displayPostAuthor">
							<ToggleControl
								label={__('Display Author', 'genesis-blocks')}
								checked={attributes.displayPostAuthor}
								onChange={() =>
									this.props.setAttributes({
										displayPostAuthor:
											!attributes.displayPostAuthor,
									})
								}
							/>
						</RenderSettingControl>
					)}
					{isPost && (
						<RenderSettingControl id="gb_postgrid_displayPostDate">
							<ToggleControl
								label={__('Display Date', 'genesis-blocks')}
								checked={attributes.displayPostDate}
								onChange={() =>
									this.props.setAttributes({
										displayPostDate:
											!attributes.displayPostDate,
									})
								}
							/>
						</RenderSettingControl>
					)}
					<RenderSettingControl id="gb_postgrid_displayPostExcerpt">
						<ToggleControl
							label={__('Display Excerpt', 'genesis-blocks')}
							checked={attributes.displayPostExcerpt}
							onChange={() =>
								this.props.setAttributes({
									displayPostExcerpt:
										!attributes.displayPostExcerpt,
								})
							}
						/>
					</RenderSettingControl>
					{attributes.displayPostExcerpt && (
						<RenderSettingControl id="gb_postgrid_excerptLength">
							<RangeControl
								label={__('Excerpt Length', 'genesis-blocks')}
								value={attributes.excerptLength}
								onChange={(value) =>
									setAttributes({ excerptLength: value })
								}
								min={0}
								max={150}
							/>
						</RenderSettingControl>
					)}
					<RenderSettingControl id="gb_postgrid_displayPostLink">
						<ToggleControl
							label={__(
								'Display Continue Reading Link',
								'genesis-blocks'
							)}
							checked={attributes.displayPostLink}
							onChange={() =>
								this.props.setAttributes({
									displayPostLink:
										!attributes.displayPostLink,
								})
							}
						/>
					</RenderSettingControl>
					{attributes.displayPostLink && (
						<RenderSettingControl id="gb_postgrid_readMoreText">
							<TextControl
								label={__(
									'Customize Continue Reading Text',
									'genesis-blocks'
								)}
								type="text"
								value={attributes.readMoreText}
								onChange={(value) =>
									this.props.setAttributes({
										readMoreText: value,
									})
								}
							/>
						</RenderSettingControl>
					)}
				</PanelBody>
				<PanelBody
					title={__('Post and Page Grid Markup', 'genesis-blocks')}
					initialOpen={false}
					className="gb-block-post-grid-markup-settings"
				>
					<RenderSettingControl id="gb_postgrid_sectionTag">
						<SelectControl
							label={__(
								'Post Grid Section Tag',
								'genesis-blocks'
							)}
							options={sectionTags}
							value={attributes.sectionTag}
							onChange={(value) =>
								this.props.setAttributes({
									sectionTag: value,
								})
							}
							help={__(
								'Change the post grid section tag to match your content hierarchy.',
								'genesis-blocks'
							)}
						/>
					</RenderSettingControl>
					{attributes.sectionTitle && (
						<RenderSettingControl id="gb_postgrid_sectionTitleTag">
							<SelectControl
								label={__(
									'Section Title Heading Tag',
									'genesis-blocks'
								)}
								options={sectionTitleTags}
								value={attributes.sectionTitleTag}
								onChange={(value) =>
									this.props.setAttributes({
										sectionTitleTag: value,
									})
								}
								help={__(
									'Change the post/page section title tag to match your content hierarchy.',
									'genesis-blocks'
								)}
							/>
						</RenderSettingControl>
					)}
					{attributes.displayPostTitle && (
						<RenderSettingControl id="gb_postgrid_postTitleTag">
							<SelectControl
								label={__(
									'Post Title Heading Tag',
									'genesis-blocks'
								)}
								options={sectionTitleTags}
								value={attributes.postTitleTag}
								onChange={(value) =>
									this.props.setAttributes({
										postTitleTag: value,
									})
								}
								help={__(
									'Change the post/page title tag to match your content hierarchy.',
									'genesis-blocks'
								)}
							/>
						</RenderSettingControl>
					)}
				</PanelBody>
			</InspectorControls>
		);
	}
}
