/**
 * WordPress dependencies
 */
import {
	Button,
	CheckboxControl,
	Dropdown,
	__experimentalVStack as VStack,
	TextControl,
	RadioControl,
	Icon,
} from '@safe-wordpress/components';
import * as Icons from '@safe-wordpress/icons';
import { __, sprintf } from '@safe-wordpress/i18n';
import { useDispatch, useSelect } from '@safe-wordpress/data';
import { useState, useMemo } from '@safe-wordpress/element';
import { store as coreStore } from '@safe-wordpress/core-data';
import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@safe-wordpress/block-editor';
import { useInstanceId } from '@safe-wordpress/compose';
import {
	TEMPLATE_POST_TYPE,
	TEMPLATE_PART_POST_TYPE,
	PATTERN_POST_TYPE,
	NAVIGATION_POST_TYPE,
	store as editorStore,
	PostPanelRow,
	PostSticky,
	PrivatePostSchedule,
} from '@safe-wordpress/editor';
type IconKey = keyof typeof Icons;

/**
 * External dependencies
 */
import clsx from 'clsx';
import { filter, keyBy, map } from 'lodash';
import { PostId, PostTypeName } from '@nelio-content/types';
import { store as NC_DATA } from '@nelio-content/data';
import { EMPTY_ARRAY } from '@nelio-content/constants';

const DESIGN_POST_TYPES = [
	TEMPLATE_POST_TYPE,
	TEMPLATE_PART_POST_TYPE,
	PATTERN_POST_TYPE,
	NAVIGATION_POST_TYPE,
];

export default function ExtendedPostStatus(): JSX.Element | null {
	const {
		allStatuses,
		canPublishPost,
		status,
		date,
		isUserSubscribed,
		password,
		postId,
		postType,
	} = useSelect( ( select ) => {
		const { getEditedPostAttribute, getCurrentPostId, getCurrentPostType } =
			select( editorStore );

		const {
			canCurrentUserPublishPost,
			getPost,
			getPostStatuses,
			isSubscribed,
		} = select( NC_DATA );

		const currentPostId = getCurrentPostId();
		const currentPostType = getCurrentPostType();
		const post =
			currentPostId !== null
				? getPost( currentPostId as PostId )
				: undefined;

		return {
			allStatuses:
				getPostStatuses( currentPostType as PostTypeName ) ||
				EMPTY_ARRAY,
			canPublishPost: canCurrentUserPublishPost( post ),
			status: getEditedPostAttribute( 'status' ) as string,
			date: getEditedPostAttribute( 'date' ) as string | null,
			password: getEditedPostAttribute( 'password' ) as string,
			isUserSubscribed: isSubscribed(),
			postId: currentPostId,
			postType: currentPostType,
		};
	}, [] );

	const [ showPassword, setShowPassword ] = useState( !! password );
	const passwordInputId = useInstanceId(
		ExtendedPostStatus,
		'editor-change-status__password-input'
	);
	const { editEntityRecord } = useDispatch( coreStore );
	const popoverProps = useMemo(
		() => ( {
			anchor: document
				.querySelector( '.editor-post-status' )
				?.closest( '.editor-post-panel__row' ),
			'aria-label': __( 'Status & visibility' ),
			headerTitle: __( 'Status & visibility' ),
			placement: 'left-start',
			offset: 36,
			shift: true,
		} ),
		[]
	);

	if ( DESIGN_POST_TYPES.includes( postType ) ) {
		return null;
	}

	const validStatuses = filter(
		allStatuses,
		( { slug } ) =>
			! [ 'nelio-content-unscheduled', 'trash' ].includes( slug )
	);
	const statuses = map(
		validStatuses,
		( {
			slug,
			name,
			core,
			description,
			available,
			icon,
			flags = [],
		} ) => ( {
			value: slug,
			label: name,
			description,
			icon,
			core,
			disabled:
				! available ||
				flags.includes( 'disabled-in-editor' ) ||
				( slug === 'future' && ! canPublishPost ),
		} )
	).filter( ( s ) => ! s.disabled );

	if ( statuses.every( ( s ) => s.core ) ) {
		return null;
	}

	if ( ! isUserSubscribed ) {
		return null;
	}

	const updatePost = ( {
		status: newStatus = status,
		password: newPassword = password,
		date: newDate = date,
	} ) => {
		void editEntityRecord( 'postType', postType, postId ?? '', {
			status: newStatus,
			date: newDate,
			password: newPassword,
		} );
	};

	const handleTogglePassword = ( value: boolean ) => {
		setShowPassword( value );
		if ( ! value ) {
			updatePost( { password: '' } );
		}
	};

	const handleStatus = ( value: string ) => {
		let newDate: string | null = date;
		let newPassword = password;
		if ( status === 'future' && date && new Date( date ) > new Date() ) {
			newDate = null;
		}
		if ( value === 'private' && password ) {
			newPassword = '';
		}
		updatePost( {
			status: value,
			date: newDate,
			password: newPassword,
		} );
	};

	const postStatusesInfo: Record<
		string,
		{ label: string; icon?: JSX.Element }
	> = keyBy(
		map( statuses, ( { value, label, icon } ) => ( {
			value,
			label,
			icon: icon ? (
				<Icon
					icon={
						( Icons as unknown as Record< IconKey, JSX.Element > )[
							icon as IconKey
						] ?? Icons.swatch
					}
				/>
			) : undefined,
		} ) ),
		'value'
	);

	const STATUS_OPTIONS = statuses.map(
		( { value, label, description } ) => ( {
			value,
			label,
			description,
		} )
	);

	return (
		<PostPanelRow
			label={ __( 'Status' ) }
			className="nelio-content-extended-post-status"
		>
			<CustomStyle />
			<Dropdown
				className="editor-post-status"
				contentClassName="editor-change-status__content"
				popoverProps={ popoverProps }
				focusOnMount
				renderToggle={ ( { onToggle, isOpen } ) => (
					<Button
						className="editor-post-status__toggle"
						variant="tertiary"
						size="compact"
						onClick={ onToggle }
						icon={ postStatusesInfo[ status ]?.icon }
						aria-label={ sprintf(
							/* translators: %s: Current post status. */
							__( 'Change status: %s' ),
							postStatusesInfo[ status ]?.label
						) }
						aria-expanded={ isOpen }
					>
						{ postStatusesInfo[ status ]?.label }
					</Button>
				) }
				renderContent={ ( { onClose } ) => (
					<>
						<InspectorPopoverHeader
							title={ __( 'Status & visibility' ) }
							onClose={ onClose }
						/>
						<form
							onSubmit={ ( event ) => {
								event.preventDefault();
								onClose();
							} }
						>
							<VStack spacing={ 4 }>
								<RadioControl
									className={ clsx(
										'editor-change-status__options',
										'nelio-content-extended-post-status__dropdown-content'
									) }
									hideLabelFromVision
									label={ __( 'Status' ) }
									options={ STATUS_OPTIONS }
									onChange={ handleStatus }
									selected={
										status === 'auto-draft'
											? 'draft'
											: status
									}
								/>
								{ status === 'future' && (
									<div className="editor-change-status__publish-date-wrapper">
										<PrivatePostSchedule
											showPopoverHeaderActions={ false }
											isCompact
										/>
									</div>
								) }
								{ status !== 'private' && (
									<VStack
										as="fieldset"
										spacing={ 4 }
										className="editor-change-status__password-fieldset"
									>
										<CheckboxControl
											label={ __( 'Password protected' ) }
											help={ __(
												'Only visible to those who know the password.'
											) }
											checked={ showPassword }
											onChange={ handleTogglePassword }
										/>
										{ showPassword && (
											<div className="editor-change-status__password-input">
												<TextControl
													label={ __( 'Password' ) }
													onChange={ ( value ) =>
														updatePost( {
															password: value,
														} )
													}
													value={ password }
													placeholder={ __(
														'Use a secure password'
													) }
													type="text"
													id={ passwordInputId }
													maxLength={ 255 }
												/>
											</div>
										) }
									</VStack>
								) }
								<PostSticky />
							</VStack>
						</form>
					</>
				) }
			/>
		</PostPanelRow>
	);
}

// =================
// HELPER COMPONENTS
// =================

// This only loads the style that hides the default editor post status panel
// if the ExtendedPostStatus is rendered.
const CustomStyle = () => (
	<style>{ `.editor-post-panel__row:has(.editor-post-status) {
			border:0;
			clip:rect(1px, 1px, 1px, 1px);
			clip-path:inset(50%);
			height:1px;
			margin:-1px;
			overflow:hidden;
			padding:0;
			position:absolute;
			width:1px;
			word-wrap:normal !important;
		}
		.nelio-content-extended-post-status.nelio-content-extended-post-status {
			border: initial;
			clip: auto;
			clip-path: none;
			height: auto;
			margin: 0;
			overflow: visible;
			padding: initial;
			position: static;
			width: 100%;
			word-wrap: normal;
			visibility: visible;
		}
		.nelio-content-extended-post-status__dropdown-content .components-radio-control__option-description {
			margin-top: 8px;
			white-space: nowrap;
			overflow: hidden;
			text-overflow: ellipsis;
		}` }</style>
);
