<?php
namespace CustomExcerptLength;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\Contracts\DataLocationsInterface;

class Module extends AbstractModule implements DataLocationsInterface {

	private const MODULE_ID      = 'custom-excerpt-length';
	private const MODULE_VERSION = '1.0.5';

	private const FILTER_PRIORITY = 99999;

	public const MAX_EXCERPT_LENGTH  = 500;
	public const MAX_READ_MORE_LENGTH = 200;
	public const DEFAULT_EXCERPT_LENGTH = 55;

	/** @var array{excerpt_length: int, read_more_text: string}|null */
	private ?array $settings_cache = null;

	private int $excerpt_length = 0;

	private string $read_more_text = '';

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Custom Excerpt Length', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Custom excerpt word counts and read-more wording for archive and blog layouts.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'frontend', 'rest' ];
	}

	protected function registerHooks(): void {
		$this->hydrateSettings();

		if ( $this->excerpt_length <= 0 && $this->read_more_text === '' ) {
			return;
		}

		if ( $this->excerpt_length > 0 ) {
			add_filter( 'excerpt_length', [ $this, 'filterExcerptLength' ], self::FILTER_PRIORITY );
		}

		if ( $this->read_more_text !== '' ) {
			add_filter( 'excerpt_more', [ $this, 'filterExcerptMore' ], self::FILTER_PRIORITY );
		}
	}

	/**
	 * @param int|string $length WordPress or third-party filters may pass non-int values.
	 */
	public function filterExcerptLength( $length ): int {
		$length = max( 0, (int) $length );

		return $this->excerpt_length > 0 ? $this->excerpt_length : $length;
	}

	/**
	 * @param string $more
	 */
	public function filterExcerptMore( $more ): string {
		$more = is_string( $more ) ? $more : '';

		if ( $this->read_more_text === '' ) {
			return $more;
		}

		return $this->buildReadMoreSuffix( $this->read_more_text );
	}

	private function buildReadMoreSuffix( string $template ): string {
		if ( ! str_contains( $template, '%link%' ) ) {
			return ' ' . esc_html( $template );
		}

		global $post;
		$link_text = __( 'Read More', 'wp-powersuite' );
		$href      = $post instanceof \WP_Post ? get_permalink( $post ) : '';
		$link      = $href !== ''
			? '<a class="wpps-excerpt-more-link" href="' . esc_url( $href ) . '">' . esc_html( $link_text ) . '</a>'
			: esc_html( $link_text );
		$parts     = explode( '%link%', $template, 2 );

		return ' ' . esc_html( $parts[0] ) . $link . esc_html( $parts[1] ?? '' );
	}

	private function hydrateSettings(): void {
		$settings             = $this->getSettings();
		$this->excerpt_length = (int) ( $settings['excerpt_length'] ?? 0 );
		$this->read_more_text = (string) ( $settings['read_more_text'] ?? '' );
	}

	/**
	 * @return array{excerpt_length: int, read_more_text: string}
	 */
	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$raw = $this->moduleSettings()->all( [] );
		$this->settings_cache = self::normalizeSettings( is_array( $raw ) ? $raw : [] );

		return $this->settings_cache;
	}

	/**
	 * @param array<string, mixed> $raw
	 * @return array{excerpt_length: int, read_more_text: string}
	 */
	public static function normalizeSettings( array $raw ): array {
		$length = isset( $raw['excerpt_length'] ) ? (int) $raw['excerpt_length'] : self::DEFAULT_EXCERPT_LENGTH;
		$length = max( 0, min( self::MAX_EXCERPT_LENGTH, $length ) );

		$read_more = '';
		if ( isset( $raw['read_more_text'] ) && is_string( $raw['read_more_text'] ) ) {
			$read_more = sanitize_text_field( $raw['read_more_text'] );
			$read_more = function_exists( 'mb_substr' )
				? mb_substr( $read_more, 0, self::MAX_READ_MORE_LENGTH )
				: substr( $read_more, 0, self::MAX_READ_MORE_LENGTH );
			$read_more = trim( $read_more );
		}

		return [
			'excerpt_length' => $length,
			'read_more_text' => $read_more,
		];
	}

	public function getDataLocations(): array {
		return [
			'option_store_keys' => [ self::MODULE_ID ],
		];
	}

	/**
	 * @return array{excerpt_length: int, read_more_text: string}
	 */
	public function getDefaultSettings(): array {
		return [
			'excerpt_length' => self::DEFAULT_EXCERPT_LENGTH,
			'read_more_text' => '',
		];
	}
}
