/**
 * External dependencies
 */
import { createMachine, sendParent } from 'xstate';
import { getQuery } from '@woocommerce/navigation';

/**
 * Internal dependencies
 */
import {
	designWithAiStateMachineContext,
	designWithAiStateMachineEvents,
	FontPairing,
	ColorPaletteResponse,
	HomepageTemplate,
} from './types';
import {
	BusinessInfoDescription,
	LookAndFeel,
	ToneOfVoice,
	ApiCallLoader,
} from './pages';
import { actions } from './actions';
import { services } from './services';
import { defaultColorPalette, fontPairings } from './prompts';

export const hasStepInUrl = (
	_ctx: unknown,
	_evt: unknown,
	{ cond }: { cond: unknown }
) => {
	const { path = '' } = getQuery() as { path: string };
	const pathFragments = path.split( '/' );
	return (
		pathFragments[ 3 ] === // [0] '', [1] 'customize-store', [2] cys step slug [3] design-with-ai step slug
		( cond as { step: string | undefined } ).step
	);
};

export const designWithAiStateMachineDefinition = createMachine(
	{
		id: 'designWithAi',
		predictableActionArguments: true,
		preserveActionOrder: true,
		schema: {
			context: {} as designWithAiStateMachineContext,
			events: {} as designWithAiStateMachineEvents,
		},
		invoke: {
			src: 'browserPopstateHandler',
		},
		on: {
			EXTERNAL_URL_UPDATE: {
				target: 'navigate',
			},
			AI_WIZARD_CLOSED_BEFORE_COMPLETION: {
				actions: [
					sendParent( ( _context, event ) => event ),
					'recordTracksStepClosed',
				],
			},
		},
		context: {
			businessInfoDescription: {
				descriptionText: '',
			},
			lookAndFeel: {
				choice: '',
			},
			toneOfVoice: {
				choice: '',
			},
			aiSuggestions: {
				// Default color palette, font pairing are used as fallbacks when the AI endpoint fails.
				defaultColorPalette: {
					default: 'Ancient Bronze',
				} as ColorPaletteResponse,
				fontPairing: 'Rubik + Inter' as FontPairing[ 'pair_name' ],
				homepageTemplate:
					'template1' as HomepageTemplate[ 'homepage_template' ],
			},
			apiCallLoader: {
				hasErrors: false,
			},
		},
		initial: 'navigate',
		states: {
			navigate: {
				always: [
					{
						target: 'businessInfoDescription',
						cond: {
							type: 'hasStepInUrl',
							step: 'business-info-description',
						},
					},
					{
						target: 'lookAndFeel',
						cond: {
							type: 'hasStepInUrl',
							step: 'look-and-feel',
						},
					},
					{
						target: 'toneOfVoice',
						cond: {
							type: 'hasStepInUrl',
							step: 'tone-of-voice',
						},
					},
					{
						target: 'apiCallLoader',
						cond: {
							type: 'hasStepInUrl',
							step: 'api-call-loader',
						},
					},
					{
						target: 'businessInfoDescription',
					},
				],
			},
			businessInfoDescription: {
				id: 'businessInfoDescription',
				initial: 'preBusinessInfoDescription',
				states: {
					preBusinessInfoDescription: {
						// if we need to prefetch options, other settings previously populated from core profiler, do it here
						always: {
							target: 'businessInfoDescription',
						},
					},
					businessInfoDescription: {
						meta: {
							component: BusinessInfoDescription,
						},
						entry: [
							{
								type: 'recordTracksStepViewed',
								step: 'business_info_description',
							},
						],
						on: {
							BUSINESS_INFO_DESCRIPTION_COMPLETE: {
								actions: [
									'assignBusinessInfoDescription',
									'spawnSaveDescriptionToOption',
								],
								target: 'postBusinessInfoDescription',
							},
						},
					},
					postBusinessInfoDescription: {
						invoke: {
							src: 'getLookAndTone',
							onError: {
								actions: [
									{
										type: 'recordTracksStepCompleted',
										step: 'business_info_description',
									},
									'logAIAPIRequestError',
								],
								target: '#lookAndFeel',
							},
							onDone: {
								actions: [
									{
										type: 'recordTracksStepCompleted',
										step: 'business_info_description',
									},
									'assignLookAndTone',
								],
								target: '#lookAndFeel',
							},
						},
					},
				},
			},
			lookAndFeel: {
				id: 'lookAndFeel',
				initial: 'preLookAndFeel',
				states: {
					preLookAndFeel: {
						always: {
							target: 'lookAndFeel',
						},
					},
					lookAndFeel: {
						meta: {
							component: LookAndFeel,
						},
						entry: [
							{
								type: 'updateQueryStep',
								step: 'look-and-feel',
							},
							{
								type: 'recordTracksStepViewed',
								step: 'look_and_feel',
							},
						],
						on: {
							LOOK_AND_FEEL_COMPLETE: {
								actions: [
									{
										type: 'recordTracksStepCompleted',
										step: 'look_and_feel',
									},
									'assignLookAndFeel',
								],
								target: 'postLookAndFeel',
							},
						},
					},
					postLookAndFeel: {
						always: {
							target: '#toneOfVoice',
						},
					},
				},
			},
			toneOfVoice: {
				id: 'toneOfVoice',
				initial: 'preToneOfVoice',
				states: {
					preToneOfVoice: {
						always: {
							target: 'toneOfVoice',
						},
					},
					toneOfVoice: {
						meta: {
							component: ToneOfVoice,
						},
						entry: [
							{
								type: 'updateQueryStep',
								step: 'tone-of-voice',
							},
							{
								type: 'recordTracksStepViewed',
								step: 'tone_of_voice',
							},
						],
						on: {
							TONE_OF_VOICE_COMPLETE: {
								actions: [
									'assignToneOfVoice',
									{
										type: 'recordTracksStepCompleted',
										step: 'tone_of_voice',
									},
								],
								target: 'postToneOfVoice',
							},
						},
					},
					postToneOfVoice: {
						always: {
							target: '#apiCallLoader',
						},
					},
				},
			},
			apiCallLoader: {
				id: 'apiCallLoader',
				initial: 'preApiCallLoader',
				states: {
					preApiCallLoader: {
						always: {
							target: 'apiCallLoader',
						},
					},
					apiCallLoader: {
						meta: {
							component: ApiCallLoader,
						},
						entry: [
							{
								type: 'updateQueryStep',
								step: 'api-call-loader',
							},
						],
						type: 'parallel',
						states: {
							chooseColorPairing: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'queryAiEndpoint',
											data: ( context ) => {
												return {
													...defaultColorPalette,
													prompt: defaultColorPalette.prompt(
														context
															.businessInfoDescription
															.descriptionText,
														context.lookAndFeel
															.choice,
														context.toneOfVoice
															.choice
													),
												};
											},
											onDone: {
												actions: [
													'assignDefaultColorPalette',
												],
												target: 'success',
											},
											// If there's an error we don't want to block the user from proceeding.
											onError: {
												target: 'success',
											},
										},
									},
									success: { type: 'final' },
								},
							},
							chooseFontPairing: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'queryAiEndpoint',
											data: ( context ) => {
												return {
													...fontPairings,
													prompt: fontPairings.prompt(
														context
															.businessInfoDescription
															.descriptionText,
														context.lookAndFeel
															.choice,
														context.toneOfVoice
															.choice
													),
												};
											},
											onDone: {
												actions: [
													'assignFontPairing',
												],
												target: 'success',
											},
											// If there's an error we don't want to block the user from proceeding.
											onError: {
												target: 'success',
											},
										},
									},
									success: { type: 'final' },
								},
							},
							updateStorePatterns: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'updateStorePatterns',
											onDone: {
												target: 'success',
											},
											onError: {
												actions: [
													'assignAPICallLoaderError',
												],
												target: '#toneOfVoice',
											},
										},
									},
									success: { type: 'final' },
								},
							},
							installAndActivateTheme: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'installAndActivateTheme',
											onDone: {
												target: 'success',
											},
											onError: {
												actions: [
													'assignAPICallLoaderError',
												],
												target: '#toneOfVoice',
											},
										},
									},
									success: { type: 'final' },
								},
							},
						},
						onDone: 'postApiCallLoader',
					},
					postApiCallLoader: {
						type: 'parallel',
						states: {
							assembleSite: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'assembleSite',
											onDone: {
												target: 'done',
											},
											onError: {
												actions: [
													'assignAPICallLoaderError',
												],
												target: '#toneOfVoice',
											},
										},
									},
									done: {
										type: 'final',
									},
								},
							},
							saveAiResponse: {
								initial: 'pending',
								states: {
									pending: {
										invoke: {
											src: 'saveAiResponseToOption',
											onDone: {
												target: 'done',
											},
											onError: {
												target: 'failed',
											},
										},
									},
									done: {
										type: 'final',
									},
									failed: {
										type: 'final',
									},
								},
							},
						},
						onDone: {
							actions: [
								// Full redirect to the Assembler Hub to ensure the user see the new generated content.
								'redirectToAssemblerHub',
							],
						},
					},
				},
			},
		},
	},
	{
		actions,
		services,
		guards: {
			hasStepInUrl,
		},
	}
);
