/**
 * WordPress dependencies
 */
import apiFetch from '@safe-wordpress/api-fetch';
import { select, subscribe } from '@safe-wordpress/data';
import { render } from '@safe-wordpress/element';
import { _x } from '@safe-wordpress/i18n';
import domReady from '@safe-wordpress/dom-ready';

/**
 * External dependencies
 */
import { debounce, keys, toPairs } from 'lodash';
import { registerCommands } from '@nab/commands';
import {
	PreviewUrlButton,
	QuotaMeter,
	TestCreationButtons,
	TitleAction,
} from '@nab/components';
import { store as NAB_DATA } from '@nab/data';
import { registerCoreExperiments } from '@nab/experiment-library';
import { store as NAB_EXPERIMENTS } from '@nab/experiments';
import {
	areEqual,
	createStagingNotice,
	logError,
	onElementReadyOrDomReadyTops,
} from '@nab/utils';
import { formatNumber } from '@nab/i18n';
import type {
	CloudAlternativeResults,
	CloudResults,
	ExperimentTypeName,
	Maybe,
	Quota,
	SubscriptionPlan,
} from '@nab/types';

/**
 * Internal dependencies
 */
import './style.scss';
import { renderHelp } from './help';
import { NoticeList } from './components/notice-list';
import DefaultIcon from '../../../images/icon.svg';

type Settings = {
	readonly staging: 'environment-type' | 'url' | false;
	readonly subscription: SubscriptionPlan | false;
};

export function initPage( _: string, settings: Settings ): void {
	registerCoreExperiments();
	onElementReadyOrDomReadyTops( '.wp-header-end', renderQuotaMeter );
	renderExperimentIconsASAP();
	updateRunningExperimentStatuses();

	domReady( () => renderPendingPageViews() );
	domReady( () => formatPageViews() );
	domReady( () => renderHeaderItems( settings ) );
	domReady( () => updateRunningExperimentStatuses() );
	domReady( () => registerCommands() );

	createStagingNotice( settings.staging );

	let previousTypes: ReadonlyArray< ExperimentTypeName > = [];

	subscribe(
		debounce( () => {
			const types = keys(
				select( NAB_EXPERIMENTS ).getExperimentTypes()
			);
			if ( areEqual( types, previousTypes ) ) {
				return;
			}
			previousTypes = types;
			renderExperimentIcons();
		}, 0 ),
		NAB_EXPERIMENTS
	);

	let prevQuota: Maybe< Quota >;
	subscribe( () => {
		const quota = select( NAB_DATA ).getQuota();
		if ( quota === prevQuota ) {
			return;
		}
		prevQuota = quota;
		updateRunningExperimentStatuses();
	}, NAB_DATA );

	renderHelp();
}

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

function renderPendingPageViews() {
	const nodes = Array.from(
		document.querySelectorAll< HTMLElement >(
			'.nab-pending-page-views-wrapper'
		)
	);

	nodes.forEach( ( node ) => {
		const id = Number.parseInt( node.getAttribute( 'data-id' ) || '' );
		if ( isNaN( id ) ) {
			return;
		}

		try {
			void apiFetch< CloudResults >( {
				path: `/nab/v1/experiment/${ id }/result`,
			} ).then( ( results ) => {
				const views = toPairs( results ).reduce(
					( acc, pair ) =>
						acc +
						( isAlternativeResults( pair ) ? pair[ 1 ].v || 0 : 0 ),
					0
				);
				node.textContent = formatNumber( views );
			} );
		} catch ( e ) {
			logError( e );
			node.textContent = _x( 'Unknown', 'text', 'nelio-ab-testing' );
		}
	} );
}

function formatPageViews() {
	const nodes = Array.from(
		document.querySelectorAll< HTMLElement >( '.nab-page-views-wrapper' )
	);
	nodes.forEach( ( node ) => {
		const value = Number.parseInt(
			node.getAttribute( 'data-value' ) || ''
		);
		node.textContent = isNaN( value ) ? '0' : formatNumber( value );
	} );
}

function renderExperimentIconsASAP() {
	renderExperimentIcons();
	setTimeout( () => {
		if ( 'complete' !== document.readyState ) {
			renderExperimentIconsASAP();
		}
	}, 100 );
}

function renderExperimentIcons() {
	const iconWrappers = Array.from(
		document.querySelectorAll< HTMLElement >( '.js-nab-experiment__icon' )
	).filter( ( wrapper ) => ! wrapper.children.length );

	iconWrappers.map( ( wrapper ) => {
		const typeName = wrapper.getAttribute( 'data-experiment-type' ) || '';
		const type = select( NAB_EXPERIMENTS ).getExperimentType( typeName );
		const Icon = ( type && type.icon ) || DefaultIcon;
		const name =
			( type && type.title ) ||
			_x( 'Unknown', 'text (experiment type)', 'nelio-ab-testing' );
		wrapper.setAttribute( 'title', name );
		return render( <Icon />, wrapper );
	} );
}

function updateRunningExperimentStatuses() {
	const quota = select( NAB_DATA ).getQuota();
	if ( ! quota ) {
		return;
	}

	const running = Array.from(
		document.querySelectorAll< HTMLElement >(
			'span.nab-experiment__status--running'
		)
	);

	const classname = 'nab-experiment__status--running-with-no-quota';
	running.forEach( ( status ) => {
		status.classList.toggle( classname, ! quota.availableQuota );
		const text = document.createElement( 'span' );
		text.textContent = _x(
			'Running',
			'text (experiment status)',
			'nelio-ab-testing'
		);
		status.innerHTML = '';
		if ( ! quota.availableQuota ) {
			const icon = document.createElement( 'span' );
			icon.classList.add( 'dashicons', 'dashicons-warning' );
			icon.setAttribute(
				'title',
				_x(
					'Event tracking has stopped because there’s no more quota available at the moment',
					'text',
					'nelio-ab-testing'
				)
			);
			status.appendChild( icon );
		}
		status.appendChild( text );
	} );
}

function renderHeaderItems( settings: Settings ) {
	const pageTitle = document.querySelector( '.wp-heading-inline' );
	if ( ! pageTitle?.parentElement ) {
		return;
	}

	const notices = document.createElement( 'div' );
	const testButtonWrapper = document.createElement( 'span' );
	const titleActionWrapper = document.createElement( 'span' );

	pageTitle.parentElement.insertBefore( notices, pageTitle.nextSibling );
	pageTitle.parentElement.insertBefore(
		titleActionWrapper,
		pageTitle.nextSibling
	);
	pageTitle.parentElement.insertBefore(
		testButtonWrapper,
		pageTitle.nextSibling
	);

	render( <NoticeList />, notices );
	render( <TestCreationButtons />, testButtonWrapper );
	render(
		<>
			<TitleAction isSubscribed={ !! settings.subscription } />
			<PreviewUrlButton />
		</>,
		titleActionWrapper
	);
}

function renderQuotaMeter() {
	const headerEnd = document.querySelector< HTMLElement >( '.wp-header-end' );
	if ( ! headerEnd?.parentElement ) {
		return;
	}
	const quotaWrapper = document.createElement( 'div' );
	render( <QuotaMeter />, quotaWrapper );
	headerEnd.parentElement.insertBefore( quotaWrapper, headerEnd );
}

const isAlternativeResults = (
	pair: [ string, unknown ]
): pair is [ string, CloudAlternativeResults ] => 'a' === pair[ 0 ][ 0 ];
