<?php

use MediaWiki\Context\IContextSource;
use MediaWiki\Html\TemplateParser;
use MediaWiki\MediaWikiServices;
use MediaWiki\Output\OutputPage;
use MediaWiki\Registration\ExtensionRegistry;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MobileFrontend\Api\ApiParseExtender;
use MobileFrontend\ContentProviders\IContentProvider;
use MobileFrontend\Features\FeaturesManager;
use MobileFrontend\Hooks\HookRunner;
use MobileFrontend\Transforms\LazyImageTransform;
use MobileFrontend\Transforms\MakeSectionsTransform;
use MobileFrontend\Transforms\MoveLeadParagraphTransform;
use MobileFrontend\Transforms\RemovableClassesTransform;
use Wikibase\Client\WikibaseClient;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Services\Lookup\TermLookupException;
use Wikimedia\IPUtils;

/**
 * Implements additional functions to use in MobileFrontend
 */
class ExtMobileFrontend {
	/**
	 * Provide alternative HTML for a user page which has not been created.
	 * Let the user know about it with pretty graphics and different texts depending
	 * on whether the user is the owner of the page or not.
	 * @internal Only for use inside MobileFrontend.
	 * @param OutputPage $out
	 * @param Title $title
	 * @return string that is empty if the transform does not apply.
	 */
	public static function blankUserPageHTML( OutputPage $out, Title $title ) {
		$pageUser = self::buildPageUserObject( $title );
		$isHidden = $pageUser && $pageUser->isHidden();
		$canViewHidden = !$isHidden || $out->getAuthority()->isAllowed( 'hideuser' );

		$out->addModuleStyles( [
			'mobile.userpage.styles', 'mobile.userpage.images'
		] );

		if ( $pageUser && !$title->exists() && $canViewHidden ) {
			return self::getUserPageContent(
				$out, $pageUser, $title );
		} else {
			return '';
		}
	}

	/**
	 * Obtains content using the given content provider and routes it to the mobile formatter
	 * if required.
	 *
	 * @param IContentProvider $provider
	 * @param OutputPage $out
	 * @param bool $mobileFormatHtml whether content should be run through the MobileFormatter
	 *
	 * @return string
	 */
	public static function domParseWithContentProvider(
		IContentProvider $provider,
		OutputPage $out,
		$mobileFormatHtml = true
	) {
		$html = $provider->getHTML();

		// If we're not running the formatter we can exit earlier
		if ( !$mobileFormatHtml ) {
			return $html;
		} else {
			return self::domParseMobile( $out, $html );
		}
	}

	/**
	 * Transforms content to be mobile friendly version.
	 * Filters out various elements and runs the MobileFormatter.
	 *
	 * @param OutputPage $out
	 * @param string $html to render.
	 *
	 * @return string
	 */
	public static function domParseMobile( OutputPage $out, $html = '' ) {
		$services = MediaWikiServices::getInstance();
		/** @var FeaturesManager $featuresManager */
		$featuresManager = $services->getService( 'MobileFrontend.FeaturesManager' );
		/** @var MobileContext $context */
		$context = $services->getService( 'MobileFrontend.Context' );
		$config = $services->getService( 'MobileFrontend.Config' );

		$title = $out->getTitle();
		$ns = $title->getNamespace();
		$action = $context->getRequest()->getText( 'action', 'view' );
		$isView = $action === 'view' || ApiParseExtender::isParseAction( $action );

		$shouldUseParsoid = false;
		if ( ExtensionRegistry::getInstance()->isLoaded( 'ParserMigration' ) ) {
			$oracle = MediaWikiServices::getInstance()->getService( 'ParserMigration.Oracle' );
			$shouldUseParsoid =
				$oracle->shouldUseParsoid( $context->getUser(), $context->getRequest(), $title );
		}

		$enableSections = (
			// Don't collapse sections e.g. on JS pages
			$title->canExist()
			&& $title->getContentModel() == CONTENT_MODEL_WIKITEXT
			// And not in certain namespaces
			&& !in_array( $ns, $config->get( 'MFNamespacesWithoutCollapsibleSections' ) )
			// And not when what's shown is not actually article text
			&& $isView
			&& !$shouldUseParsoid
		);

		// https://phabricator.wikimedia.org/T232690
		if ( !MobileFormatter::canApply( $html, $config->get( 'MFMobileFormatterOptions' ) ) ) {
			// In future we might want to prepend a message feeding
			// back to the user that the page is not mobile friendly.
			return $html;
		}

		$formatter = new MobileFormatter( $html );

		$hookRunner = new HookRunner( $services->getHookContainer() );
		$hookRunner->onMobileFrontendBeforeDOM( $context, $formatter );

		$shouldLazyTransformImages = $featuresManager->isFeatureAvailableForCurrentUser( 'MFLazyLoadImages' );
		$leadParagraphEnabled = in_array( $ns, $config->get( 'MFNamespacesWithLeadParagraphs' ) );
		$showFirstParagraphBeforeInfobox = $leadParagraphEnabled &&
			$featuresManager->isFeatureAvailableForCurrentUser( 'MFShowFirstParagraphBeforeInfobox' );

		$transforms = [];
		// Remove specified content in content namespaces
		if ( in_array( $title->getNamespace(), $config->get( 'ContentNamespaces' ), true ) ) {
			$mfRemovableClasses = $config->get( 'MFRemovableClasses' );
			$removableClasses = $mfRemovableClasses['base'];
			if ( $context->isBetaGroupMember() ) {
				$removableClasses = array_unique(
					array_merge( $removableClasses, $mfRemovableClasses['beta'] )
				);
			}

			$transforms[] = new RemovableClassesTransform( $removableClasses );
		}

		if ( $enableSections ) {
			$options = $config->get( 'MFMobileFormatterOptions' );
			$topHeadingTags = $options['headings'];

			$transforms[] = new MakeSectionsTransform(
				$topHeadingTags,
				true
			);
		}

		if ( $shouldLazyTransformImages ) {
			$transforms[] = new LazyImageTransform( $config->get( 'MFLazyLoadSkipSmallImages' ) );
		}

		if ( $showFirstParagraphBeforeInfobox ) {
			$transforms[] = new MoveLeadParagraphTransform( $title, $title->getLatestRevID() );
		}

		$start = microtime( true );
		$formatter->applyTransforms( $transforms );
		$end = microtime( true );
		$report = sprintf( "MobileFormatter took %.3f seconds", $end - $start );

		return $formatter->getText() . "\n<!-- $report -->";
	}

	/**
	 * Return new User object based on username or IP address.
	 * @param Title $title
	 * @return User|null
	 */
	private static function buildPageUserObject( Title $title ) {
		$titleText = $title->getText();

		$usernameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
		if ( $usernameUtils->isIP( $titleText ) || IPUtils::isIPv6( $titleText ) ) {
			return User::newFromAnyId( null, $titleText, null );
		}

		$user = User::newFromName( $titleText );
		if ( $user && $user->isRegistered() ) {
			return $user;
		}

		return null;
	}

	/**
	 * Generate user page content for non-existent user pages
	 *
	 * @param IContextSource $output
	 * @param User $pageUser owner of the user page
	 * @param Title $title
	 * @return string
	 */
	protected static function getUserPageContent( IContextSource $output,
		User $pageUser, Title $title
	) {
		/** @var MobileContext $context */
		$context = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
		$pageUsername = $pageUser->getName();
		// Is the current user viewing their own page?
		$isCurrentUser = $output->getUser()->getName() === $pageUsername;

		$data = [
			'userImageClass' => 'userpage-image-placeholder',
		];
		$data['ctaHeading'] = $isCurrentUser ?
			$context->msg( 'mobile-frontend-user-page-no-owner-page-yet' )->text() :
			$context->msg( 'mobile-frontend-user-page-no-page-yet', $pageUsername )->parse();
		$data['ctaDescription'] = $isCurrentUser ?
			$context->msg(
				'mobile-frontend-user-page-describe-yourself',
				$context->msg( 'mobile-frontend-user-page-describe-yourself-editors' )->text()
			)->text() :
			$context->msg( 'mobile-frontend-user-page-desired-action', $pageUsername )->parse();
		$data['createPageLinkLabel'] = $isCurrentUser ?
			$context->msg( 'mobile-frontend-user-page-create-owner-page-link-label' )->text() :
			$context->msg(
				'mobile-frontend-user-page-create-user-page-link-label',
				$pageUser->getUserPage()->getBaseTitle()
			)->parse();
		// Mobile editor has trouble when section is not specified.
		// It doesn't matter here since the page doesn't exist.
		$data['editUrl'] = $title->getLinkURL( [ 'action' => 'edit', 'section' => 0 ] );
		$data['editSection'] = 0;
		$data['createPageLinkAdditionalClasses'] = $isCurrentUser ?
			'cdx-button cdx-button--action-progressive cdx-button--weight-primary' : '';

		$templateParser = new TemplateParser( __DIR__ . '/templates' );
		return $templateParser->processTemplate( 'UserPageCta', $data );
	}

	/**
	 * Returns a short description of a page from Wikidata
	 *
	 * @param string $item Wikibase id of the page
	 * @return string|null
	 */
	public static function getWikibaseDescription( $item ) {
		if ( !ExtensionRegistry::getInstance()->isLoaded( 'WikibaseClient' ) ) {
			return null;
		}

		$contLang = MediaWikiServices::getInstance()->getContentLanguage();
		$termLookup = WikibaseClient::getTermLookup();
		try {
			$itemId = new ItemId( $item );
		} catch ( InvalidArgumentException $exception ) {
			return null;
		}

		try {
			return $termLookup->getDescription( $itemId, $contLang->getCode() );
		} catch ( TermLookupException $exception ) {
			return null;
		}
	}
}
