<?php

namespace CirrusSearch;

use CirrusSearch\Extra\MultiList\MultiListBuilder;
use Exception;
use MediaWiki\Extension\EventBus\EventBusFactory;
use MediaWiki\Page\ProperPageIdentity;
use MediaWiki\Page\WikiPageFactory;

class EventBusWeightedTagsUpdater implements WeightedTagsUpdater {

	private EventBusFactory $eventBusFactory;
	private EventBusWeightedTagSerializer $eventSerializer;
	private WikiPageFactory $wikiPageFactory;

	/**
	 * @param EventBusFactory $eventBusFactory
	 * @param EventBusWeightedTagSerializer $eventSerializer
	 * @param WikiPageFactory $wikiPageFactory
	 */
	public function __construct(
		EventBusFactory $eventBusFactory,
		EventBusWeightedTagSerializer $eventSerializer,
		WikiPageFactory $wikiPageFactory
	) {
		$this->eventBusFactory = $eventBusFactory;
		$this->eventSerializer = $eventSerializer;
		$this->wikiPageFactory = $wikiPageFactory;
	}

	/**
	 * @inheritDoc
	 */
	public function resetWeightedTags(
		ProperPageIdentity $page,
		array $tagPrefixes,
		?string $trigger = null
	): void {
		$event = $this->eventSerializer->toClearEvent(
			$this->wikiPageFactory->newFromTitle( $page ),
			$tagPrefixes,
			$trigger === 'revision' ? true : null
		);
		try {
			$outcome = $this->eventBusFactory->getInstanceForStream( EventBusWeightedTagSerializer::STREAM )->send(
				$event
			);
		} catch ( Exception $e ) {
			throw new WeightedTagsException(
				"Failed to send reset: {msg}",
				[ 'msg' => $e->getMessage() ], $e
			);
		}
		if ( $outcome !== true ) {
			throw new WeightedTagsException(
				"Failed to send reset: {msg}",
				[ 'msg' => var_export( $outcome, true ) ]
			);
		}
	}

	/**
	 * @inheritDoc
	 */
	public function updateWeightedTags(
		ProperPageIdentity $page,
		string $tagPrefix,
		?array $tagWeights = null,
		?string $trigger = null
	): void {
		$weightedTags = MultiListBuilder::buildWeightedTags( $tagPrefix, $tagWeights );

		$set = array_reduce( $weightedTags, static function ( $set, $weightedTag ) {
			if ( !isset( $set[$weightedTag->getPrefix()] ) ) {
				$set[$weightedTag->getPrefix()] = [];
			}
			$mappedWeightedTag = [ 'tag' => $weightedTag->getName() ];
			$weight = $weightedTag->getWeight();
			if ( $weight !== null ) {
				$mappedWeightedTag['score'] = $weight / 1000;
			}
			$set[$weightedTag->getPrefix()][] = $mappedWeightedTag;
			return $set;
		}, [] );

		$event = $this->eventSerializer->toSetEvent(
			$this->wikiPageFactory->newFromTitle( $page ), $set, $trigger === 'revision' ? true : null );

		try {
			$outcome = $this->eventBusFactory->getInstanceForStream( EventBusWeightedTagSerializer::STREAM )->send(
				$event
			);
		} catch ( Exception $e ) {
			throw new WeightedTagsException(
				"Failed to send update: {msg}",
				[ 'msg' => $e->getMessage() ], $e
			);
		}
		if ( $outcome !== true ) {
			throw new WeightedTagsException(
				"Failed to send update: {msg}",
				[ 'msg' => var_export( $outcome, true ) ]
			);
		}
	}

}
