/**
 * External dependencies
 */
import deepMerge from 'deepmerge';

/**
 * WordPress dependencies
 */
import { useCallback, useState } from '@wordpress/element';

/**
 * Internal dependencies
 */
import DataForm from '../index';
import type { Field } from '../../types';

const DataAdapterComponent = () => {
	type DataAdapterItem = {
		user: {
			profile: {
				name: string;
				email: string;
			};
			preferences: {
				notifications: boolean;
			};
		};
		revenue: {
			total: number;
			units: number;
			pricePerUnit: number;
		};
	};

	const [ data, setData ] = useState< DataAdapterItem >( {
		user: {
			profile: {
				name: 'John Doe',
				email: 'john@example.com',
			},
			preferences: {
				notifications: true,
			},
		},
		revenue: {
			total: 30,
			units: 10,
			pricePerUnit: 3,
		},
	} );

	const nestedFields: Field< DataAdapterItem >[] = [
		// Examples of autogenerated getValue/setValue methods
		// for nested data based on the field id.
		{
			id: 'user.profile.name',
			label: 'User name',
			type: 'text',
		},
		{
			id: 'user.profile.email',
			label: 'User email',
			type: 'email',
		},
		{
			id: 'user.profile.tags',
			label: 'User tags',
			type: 'array',
			placeholder: 'Enter comma-separated tags',
			description:
				'Add tags separated by commas (e.g., "tag1, tag2, tag3")',
			elements: [
				{ value: 'astronomy', label: 'Astronomy' },
				{ value: 'book-review', label: 'Book review' },
				{ value: 'event', label: 'Event' },
				{ value: 'photography', label: 'Photography' },
				{ value: 'travel', label: 'Travel' },
			],
		},
		// Example of adapting a data value to a control value
		// by providing getValue/setValue methods.
		{
			id: 'user.preferences.notifications',
			label: 'Notifications',
			type: 'boolean',
			Edit: 'radio',
			elements: [
				{ label: 'Enabled', value: 'enabled' },
				{ label: 'Disabled', value: 'disabled' },
			],
			getValue: ( { item } ) =>
				item.user.preferences.notifications === true
					? 'enabled'
					: 'disabled',
			setValue: ( { value } ) => ( {
				user: {
					preferences: { notifications: value === 'enabled' },
				},
			} ),
		},
		// Example of deriving data by leveraging setValue method.
		{
			id: 'revenue.total',
			label: 'Total revenue',
			type: 'integer',
			readOnly: true,
		},
		{
			id: 'revenue.pricePerUnit',
			label: 'Price per unit',
			type: 'integer',
			setValue: ( { item, value } ) => ( {
				revenue: {
					total: value * item.revenue.units,
					pricePerUnit: value,
				},
			} ),
		},
		{
			id: 'revenue.units',
			label: 'Units',
			type: 'integer',
			setValue: ( { item, value } ) => ( {
				revenue: {
					total: item.revenue.pricePerUnit * value,
					units: value,
				},
			} ),
		},
	];

	const handleChange = useCallback( ( edits: any ) => {
		// Edits will respect the shape of the data
		// because fields provide the proper information
		// (via field.id or via field.setValue).
		setData( ( prev ) =>
			deepMerge( prev, edits, {
				arrayMerge: ( target, source ) => source,
			} )
		);
	}, [] );

	return (
		<>
			<h1>Data adapter</h1>
			<p>
				This story is best looked at with the code on the side. It aims
				to highlight how DataForm can wrangle data in scenarios such as
				nested data, bridge data to/from UI controls, and derived data.
			</p>
			<p>
				<b>Current data snapshot:</b>
			</p>
			<pre>{ JSON.stringify( data, null, 2 ) }</pre>
			<h2>Nested data</h2>
			<p>
				The first example demonstrates how to signal nested data via{ ' ' }
				<code>field.id</code>.
			</p>
			<p>
				By using <code>{ `{ id: 'user.profile.name' }` }</code> as field
				id, when users edit the name, the edits will come in this shape:
				<code>{ `{ user: { profile: { name: 'John Doe' } } }` }</code>
			</p>
			<DataForm< DataAdapterItem >
				data={ data }
				fields={ nestedFields }
				form={ {
					layout: {
						type: 'panel',
						labelPosition: 'top',
						openAs: 'modal',
					},
					fields: [
						{
							id: 'userProfile',
							label: 'User profile',
							children: [
								'user.profile.name',
								'user.profile.email',
								'user.profile.tags',
							],
						},
					],
				} }
				onChange={ handleChange }
			/>
			<h2>Adapt data and UI control</h2>
			<p>
				Sometimes, we need to adapt the data type to and from the UI
				control response. This example demonstrates how to adapt a
				boolean to a text string (Enabled/Disabled).
			</p>
			<DataForm< DataAdapterItem >
				data={ data }
				fields={ nestedFields }
				form={ {
					layout: {
						type: 'panel',
						labelPosition: 'top',
						openAs: 'modal',
					},
					fields: [ 'user.preferences.notifications' ],
				} }
				onChange={ handleChange }
			/>
			<h2>Derived data</h2>
			<p>
				Last, but not least, this example showcases how to work with
				derived data by providing a custom setValue function. Note how,
				changing UNITS or PRICE PER UNIT, updates the TOTAL value as
				well.
			</p>
			<DataForm< DataAdapterItem >
				data={ data }
				fields={ nestedFields }
				form={ {
					layout: {
						type: 'panel',
						labelPosition: 'top',
						openAs: 'modal',
					},
					fields: [
						{
							id: 'revenue',
							label: 'Revenue',
							children: [
								'revenue.pricePerUnit',
								'revenue.units',
								'revenue.total',
							],
						},
					],
				} }
				onChange={ handleChange }
			/>
		</>
	);
};

export default DataAdapterComponent;
