/**
 * WordPress dependencies
 */
import apiFetch from '@safe-wordpress/api-fetch';
import { select, dispatch, subscribe } from '@safe-wordpress/data';
import { render } from '@safe-wordpress/element';

/**
 * External dependencies
 */
import { filter, isEqual, map, trim } from 'lodash';
import {
	getDisabledProfiles,
	processHtml,
	store as NC_EDIT_POST,
} from '@nelio-content/edit-post';
import { store as NC_DATA } from '@nelio-content/data';
import { store as NC_SOCIAL_EDITOR } from '@nelio-content/social-message-editor';
import { getLinks, isEmpty } from '@nelio-content/utils';
import type {
	AuthorId,
	AutoShareEndMode,
	AwsPost,
	Dict,
	EditingPost,
	EditorialReference,
	Maybe,
	PostId,
	Url,
} from '@nelio-content/types';

/**
 * Internal dependencies
 */
import { addListeners } from './listeners';

// =======
// EXPORTS
// =======

export type Args = {
	readonly attributes: {
		readonly externalFeatImage: {
			readonly url: Url;
			readonly alt: string;
		};
		readonly followers: ReadonlyArray< AuthorId >;
		readonly references: ReadonlyArray< EditorialReference >;
	};
	readonly postId: PostId;
	readonly settings: Settings;
};

export type Settings = {
	readonly isClassicEditor?: boolean;
	readonly isElementorEditor?: boolean;
	readonly nonce: string;
	readonly qualityAnalysis: QualityAnalysisSettings;
	readonly autoShareEndModes: ReadonlyArray< AutoShareEndMode >;
	readonly shouldAuthorBeFollower: boolean;
};

export type QualityAnalysisSettings = {
	readonly canImageBeAutoSet: boolean;
	readonly isFullyIntegrated: boolean;
	readonly isYoastIntegrated: boolean;
	readonly supportsFeatImage: boolean;
};

export async function init( args: Args ): Promise< void > {
	const post = await loadPostFromWordPress( args.postId );
	await initPost( {
		...post,
		date: 'none' === post.date ? false : post.date,
		followers: args.attributes.followers,
		statistics: {
			engagement: {},
			pageviews: {},
		},
	} );
	setEditorType(
		args.settings.isClassicEditor,
		args.settings.isElementorEditor
	);

	addTinyMCEGlobal();
	initAutoShareEndModes( args.settings.autoShareEndModes );
	initDefaultFollowers( args.settings.shouldAuthorBeFollower );
	initExternalFeaturedImage( args.attributes.externalFeatImage );
	initReferences( args.attributes.references );
	customizeQualityAnalysis( args.settings.qualityAnalysis );

	addListeners();

	keepRelatedItemsSynchronized();
}

export function listenToEditPostStore(
	callback: ( values: Dict ) => void
): void {
	let prevValues = {};
	// eslint-disable-next-line @typescript-eslint/no-unsafe-call
	subscribe( () => {
		const {
			getAutoShareEndMode,
			getAutomationSources,
			getExternalFeaturedImageAlt,
			getExternalFeaturedImageUrl,
			getNetworkImageIds,
			getPost,
			getQueryArgs,
			getSeries,
			getSuggestedReferences,
			isAutoShareEnabled,
		} = select( NC_EDIT_POST );

		const post = getPost();
		const { highlights } = processHtml( post.content );
		const values = {
			autoShareEndMode: getAutoShareEndMode(),
			automationSources: getAutomationSources(),
			efiAlt: getExternalFeaturedImageAlt(),
			efiUrl: getExternalFeaturedImageUrl() || ( '' as Url ),
			followers: post.followers,
			highlights,
			isAutoShareEnabled: isAutoShareEnabled(),
			networkImageIds: getNetworkImageIds(),
			permalinkQueryArgs: getQueryArgs(),
			series: getSeries(),
			suggestedReferences: getSuggestedReferences(),
		};

		if ( ! isEqual( values, prevValues ) ) {
			callback( values );
		}

		prevValues = values;
	}, NC_EDIT_POST );
}

export function renderMetaBox(
	id: string,
	Component: () => JSX.Element | null
): void {
	const el = document.getElementById( id );
	if ( ! el ) {
		return;
	}

	const wrapper = el.querySelector( '.inside' );
	if ( ! wrapper ) {
		return;
	}

	render( <Component />, wrapper );
}

export function createSocialMessageUsingSelection(
	text: string,
	links: ReadonlyArray< Url >
): void {
	text = trim( text );
	if ( isEmpty( text ) ) {
		return;
	}

	const twitter = searchTwitterHandleToMention( links );
	if ( twitter ) {
		text = `${ text } /cc ${ twitter }`;
	}

	text += ' {permalink}';

	const { getPost } = select( NC_EDIT_POST );
	const { openNewSocialMessageEditor } = dispatch( NC_SOCIAL_EDITOR );

	void openNewSocialMessageEditor(
		{ text },
		{
			context: 'post',
			post: getPost(),
			disabledProfileIds: getDisabledProfiles(),
		}
	);
}

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

function addTinyMCEGlobal() {
	// eslint-disable-next-line
	( window as any ).NelioContentTinyMCE = {
		createMessage: createSocialMessageUsingSelection,
		getLinks,
		isEmpty,
	};
}

function searchTwitterHandleToMention( links: ReadonlyArray< Url > ) {
	if ( isEmpty( links ) ) {
		return false;
	}

	const { getSocialProfilesByNetwork } = select( NC_DATA );
	if ( isEmpty( getSocialProfilesByNetwork( 'twitter' ) ) ) {
		return false;
	}

	const { getReferenceByUrl } = select( NC_EDIT_POST );
	for ( const link of links ) {
		const data = getReferenceByUrl( link );
		if ( data && data.twitter ) {
			return data.twitter;
		}
	}

	return false;
}

export async function loadPostFromWordPress(
	postId: PostId
): Promise< AwsPost > {
	return await apiFetch< AwsPost >( {
		path: `/nelio-content/v1/post/${ postId }?aws`,
	} );
}

async function initPost( post: EditingPost ) {
	const { loadPostItems, setPost } = dispatch( NC_EDIT_POST );
	await setPost( post );
	void loadPostItems();
}

function initDefaultFollowers( shouldAuthorBeFollower: boolean ) {
	const { includeAuthorInFollowers } = dispatch( NC_EDIT_POST );
	void includeAuthorInFollowers( shouldAuthorBeFollower );
}

function initAutoShareEndModes(
	autoShareEndModes: ReadonlyArray< AutoShareEndMode >
) {
	if ( isEmpty( autoShareEndModes ) ) {
		return;
	}
	const { setAutoShareEndModes } = dispatch( NC_EDIT_POST );
	void setAutoShareEndModes( autoShareEndModes );
}

function initExternalFeaturedImage( {
	url,
	alt,
}: Args[ 'attributes' ][ 'externalFeatImage' ] ) {
	if ( ! url ) {
		return;
	}
	const { setExternalFeaturedImage } = dispatch( NC_EDIT_POST );
	void setExternalFeaturedImage( url, alt );
}

function initReferences( references: ReadonlyArray< EditorialReference > ) {
	const { receiveReferences, suggestReferences } = dispatch( NC_EDIT_POST );
	void receiveReferences( references );

	const suggestions = filter( references, { isSuggestion: true } );
	if ( ! isEmpty( suggestions ) ) {
		void suggestReferences( map( suggestions, 'url' ) );
	}
}

function customizeQualityAnalysis( quality: QualityAnalysisSettings ) {
	updateQualityAnalysisIntegration( quality );
	updateYoastCheck( quality );
	updateFeaturedImageCheck( quality );
	updateAuthorCheck();
}

function updateQualityAnalysisIntegration( quality: QualityAnalysisSettings ) {
	const { markQualityAnalysisAsFullyIntegrated } = dispatch( NC_EDIT_POST );
	void markQualityAnalysisAsFullyIntegrated( !! quality.isFullyIntegrated );
}

function updateYoastCheck( { isYoastIntegrated }: QualityAnalysisSettings ) {
	if ( isYoastIntegrated ) {
		return;
	}
	const { removeQualityCheckType } = dispatch( NC_EDIT_POST );
	void removeQualityCheckType( 'nelio-content/yoast-content' );
	void removeQualityCheckType( 'nelio-content/yoast-seo' );
}

function updateFeaturedImageCheck( quality: QualityAnalysisSettings ) {
	const { supportsFeatImage, canImageBeAutoSet } = quality;
	if ( ! supportsFeatImage ) {
		const { removeQualityCheckType } = dispatch( NC_EDIT_POST );
		void removeQualityCheckType( 'nelio-content/featured-image' );
		return;
	}
	const { updateQualityCheckSettings } = dispatch( NC_EDIT_POST );
	void updateQualityCheckSettings( 'nelio-content/featured-image', {
		canImageBeAutoSet,
	} );
}

function updateAuthorCheck() {
	const { isMultiAuthor } = select( NC_DATA );
	if ( isMultiAuthor() ) {
		return;
	}
	const { removeQualityCheckType } = dispatch( NC_EDIT_POST );
	void removeQualityCheckType( 'nelio-content/author' );
}

function keepRelatedItemsSynchronized() {
	let prevDate: Maybe< string >, prevStatus: string;
	// eslint-disable-next-line @typescript-eslint/no-unsafe-call
	subscribe( () => {
		const { getDate, getStatus } = select( NC_EDIT_POST );

		const date = getDate();
		const status = getStatus();

		if ( date === prevDate && status === prevStatus ) {
			return;
		}
		prevDate = date;
		prevStatus = status;

		const { updateDatesInPostRelatedItems } = dispatch( NC_EDIT_POST );
		void updateDatesInPostRelatedItems();
	} );
}

function setEditorType( isClassicEditor = false, isElementorEditor = false ) {
	const { setEditorToClassic, setEditorToElementor } =
		dispatch( NC_EDIT_POST );
	void setEditorToClassic( isClassicEditor );
	void setEditorToElementor( isElementorEditor );
}
