/**
 * WordPress dependencies
 */
import {
	BaseControl,
	SelectControl,
	TextControl,
} from '@safe-wordpress/components';
import { useState } from '@safe-wordpress/element';
import { _x, sprintf } from '@safe-wordpress/i18n';

/**
 * External dependencies
 */
import clsx from 'clsx';
import { last, noop } from 'lodash';
import { Editor } from 'primereact/editor';
import { CodeEditor } from '@nab/code-editor';
import { getElementLabel, hasHead, isDefined } from '@nab/utils';

/**
 * Internal dependencies
 */
import './style.scss';
import type { AlternativeAttributes } from '../../types';

export type ValuePreviewProps = {
	readonly attributes: AlternativeAttributes;
};

export const ValuePreview = ( {
	attributes: { css, content },
}: ValuePreviewProps ): JSX.Element => {
	const [ tab, setTab ] = useState< Tab[ 'name' ] >( 'css' );
	const tabs = [
		!! css.trim ? TABS.css : undefined,
		!! content.length ? TABS.content : undefined,
	].filter( isDefined );

	const [ contentSelector, setContentSelector ] = useState(
		content[ 0 ]?.selector ?? ''
	);

	return (
		<TabPanel
			className="nab-css-and-text-preview"
			activeClass="nab-css-and-text-preview__tab--active"
			orientation="horizontal"
			tabs={ hasHead( tabs ) ? tabs : [ TABS.css ] }
			activeTab={ tab }
			onSelect={ setTab }
		>
			{ ( { name }: Tab ): JSX.Element => {
				switch ( name ) {
					case 'content':
						return (
							<>
								<SelectControl
									label={ _x(
										'Element',
										'text',
										'nelio-ab-testing'
									) }
									options={ content.map( ( c ) => ( {
										value: c.selector,
										label: sprintf(
											'%1$s – %2$s',
											getElementLabel( c.selector ),
											c.selector
										),
									} ) ) }
									value={ contentSelector }
									onChange={ setContentSelector }
								/>
								<ContentView
									value={ content.find(
										( c ) => c.selector === contentSelector
									) }
								/>
							</>
						);

					case 'css':
						return (
							<CodeEditor
								className="nab-css-value-preview"
								language="css"
								value={ css }
								readOnly
							/>
						);
				}
			} }
		</TabPanel>
	);
};

// ============
// HELPER VIEWS
// ============

const ContentView = ( {
	value,
}: {
	readonly value?: AlternativeAttributes[ 'content' ][ number ];
} ) => {
	switch ( value?.type ) {
		case undefined:
			return null;

		case 'element':
			return (
				<BaseControl
					id="nab-css-and-text-preview__content-change"
					label={ _x( 'Content', 'text', 'nelio-ab-testing' ) }
				>
					<Editor
						className={
							[ 'ol', 'ul' ].includes(
								getTagName( value.selector )
							)
								? `nab-ql-editor nab-ql-editor--has-${ getTagName(
										value.selector
								  ) }-list`
								: 'nab-ql-editor'
						}
						value={ value.html }
						onTextChange={ noop }
						headerTemplate={ [] }
						readOnly
					/>
				</BaseControl>
			);

		case 'image':
			return (
				<>
					<TextControl
						label={ _x( 'Image URL', 'text', 'nelio-ab-testing' ) }
						value={ value.src }
						onChange={ noop }
						readOnly
					/>
					<TextControl
						label={ _x( 'Alt Text', 'text', 'nelio-ab-testing' ) }
						value={ value.alt }
						onChange={ noop }
						readOnly
					/>
				</>
			);
	}
};

type TabPanelProps = {
	readonly className: string;
	readonly activeClass: string;
	readonly orientation: 'horizontal';
	readonly onSelect: ( tab: Tab[ 'name' ] ) => void;
	readonly children: ( tab: Tab ) => JSX.Element;
	readonly activeTab: Tab[ 'name' ];
	readonly tabs: Readonly< [ Tab, ...Tab[] ] >;
};

// NOTICE. This is a workaround until “TabPanel” can be controlled/uncontrolled.
const TabPanel = ( {
	className,
	activeClass,
	orientation,
	onSelect,
	children,
	activeTab,
	tabs,
}: TabPanelProps ) => {
	if ( 1 === tabs.length ) {
		return children( tabs[ 0 ] );
	}

	return (
		<div className={ className }>
			<div
				role="tablist"
				aria-orientation={ orientation }
				className="components-tab-panel__tabs"
			>
				{ tabs.map( ( tab ) => (
					<button
						key={ tab.name }
						type="button"
						role="tab"
						tabIndex={ tab.name === activeTab ? -1 : undefined }
						aria-selected="false"
						id={ `tab-panel-1-${ tab.name }` }
						aria-controls={ `tab-panel-1-${ tab.name }-view` }
						className={ clsx( {
							'components-button components-tab-panel__tabs-item':
								true,
							[ tab.className ]: true,
							[ activeClass ]: tab.name === activeTab,
						} ) }
						onClick={ () => onSelect( tab.name ) }
					>
						{ tab.title }
					</button>
				) ) }
			</div>
			<div
				aria-labelledby={ `tab-panel-1-${
					activeTab || tabs[ 0 ].name
				}` }
				role="tabpanel"
				id={ `tab-panel-1-${ activeTab || tabs[ 0 ].name }-view` }
				className="components-tab-panel__tab-content"
			>
				{ children(
					tabs.find( ( t ) => t.name === activeTab ) || tabs[ 0 ]
				) }
			</div>
		</div>
	);
};

// ====
// DATA
// ====

type Tab = {
	readonly name: 'css' | 'content';
	readonly title: string;
	readonly className: string;
};

const TABS: Record< Tab[ 'name' ], Tab > = {
	css: {
		name: 'css',
		title: _x( 'CSS', 'text', 'nelio-ab-testing' ),
		className: 'nab-css-and-text-preview__tab nab-css__tab',
	},
	content: {
		name: 'content',
		title: _x( 'Content', 'text', 'nelio-ab-testing' ),
		className: 'nab-css-and-text-preview__tab nab-content__tab',
	},
};

// =======
// HELPERS
// =======

const getTagName = ( selector: string ) =>
	last( selector.split( ' ' ) )?.split( /[#.:]/ )[ 0 ] || '';
