<?php
/**
 * File Path: includes/helpers/mbs-sort-helpers.php
 *
 * Sort Key Helpers — F-SORT-1
 *
 * Provides mbs_compute_sort_key(): computes an article-ignoring, natural-numeric
 * sort key for term names and post titles. Keys are stored at write time so that
 * MySQL ORDER BY meta_value ASC produces correct ordering without runtime PHP work.
 *
 * Loaded at bootstrap level (bookshowroom.php) so it is available both during
 * DB migrations (plugins_loaded) and for the term / post save hooks (init+).
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Helpers
 * @since      1.1.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Compute an article-ignoring, natural-numeric sort key.
 *
 * Algorithm:
 *   1. Lowercase + trim (UTF-8-safe).
 *   2. Strip one leading article from a multilingual list (EN/FR/DE/ES/IT/NL).
 *      Longer articles are checked before shorter ones to prevent prefix collisions
 *      (e.g. "les " is checked before "le " so "Les Misérables" → "misérables",
 *      not "s misérables").
 *   3. Zero-pad every digit sequence to 8 characters so that lexicographic order
 *      equals natural numeric order ("Season 00000002" < "Season 00000010").
 *
 * Examples:
 *   "The Hobbit"                    → "hobbit"
 *   "A Game of Thrones"             → "game of thrones"
 *   "Les Misérables"                → "misérables"
 *   "L'Homme qui rit"               → "homme qui rit"
 *   "Die Leiden des jungen Werthers"→ "leiden des jungen werthers"
 *   "Season 2"                      → "season 00000002"
 *   "Season 10"                     → "season 00000010"
 *   "van der Berg"                  → "van der berg"  (no article stripped)
 *
 * @param  string $raw  Display name (term name, post title, etc.).
 * @return string       Lowercase sort key, safe for MySQL ORDER BY meta_value ASC.
 */
function mbs_compute_sort_key( string $raw ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix
	$key = mb_strtolower( trim( $raw ), 'UTF-8' );

	// Leading articles — checked longest-first within each language group to prevent
	// prefix collisions. Only the first match is stripped (one article per title).
	$articles = array(
		// English
		'the ',
		'an ',
		'a ',
		// French / Italian (elided — no space, e.g. "l'homme")
		"l'",
		// French
		'les ', 'le ', 'la ', 'une ', 'un ',
		// German — inflected indefinite (longest first)
		'einen ', 'einem ', 'einer ', 'eines ',
		'eine ',
		// German — definite (longest first)
		'die ', 'das ', 'der ', 'den ', 'dem ', 'des ',
		// German — indefinite masculine/neuter
		'ein ',
		// Spanish
		'unas ', 'unos ',
		'los ', 'las ', 'una ',
		'el ',
		// Italian (l' already covered; shared la/le/un/una already above)
		'gli ', 'lo ', 'il ',
		// Dutch
		'het ', 'een ', 'de ',
	);

	foreach ( $articles as $article ) {
		if ( str_starts_with( $key, $article ) ) {
			$key = ltrim( substr( $key, strlen( $article ) ) );
			break; // Only strip one leading article.
		}
	}

	// Zero-pad all digit sequences to 8 characters for natural numeric sort.
	$key = preg_replace_callback(
		'/\d+/',
		static function ( array $m ): string {
			return str_pad( $m[0], 8, '0', STR_PAD_LEFT );
		},
		$key
	);

	return (string) $key;
}
