<?php
/**
 * File Path: includes/shortcodes/class-mbs-shortcode-author-directory.php
 *
 * My Book Showroom — Author Directory Shortcode
 *
 * [mbs_author_directory] — renders a browsable directory of all mbs_author
 * taxonomy terms. PRO only.
 *
 * Each card shows:
 *   - Author photo (or placeholder)
 *   - Author name (links to taxonomy archive)
 *   - Book count (when show_count=true)
 *   - Bio snippet (when show_bio=true and mbs_author_bio is populated)
 *   - Website link (when show_website=true and mbs_author_website_url is set)
 *   - Featured badge (when mbs_author_featured=1)
 *
 * Featured authors always sort before non-featured when show_featured_first=true
 * (the default). Within each group the standard orderby/order applies.
 *
 * An A-Z filter strip (show_filter=true, the default) renders letter buttons
 * above the grid. Clicking a letter hides cards whose author name does not start
 * with that letter. Filtering is client-side via mbs-author-directory.js.
 * Each card carries a data-initial attribute set to the lower-case first letter
 * of the author name so the JS can read it without DOM inspection.
 *
 * Attributes:
 *   columns            (int,  default 3)    CSS grid column count.
 *   show_count         (bool, default true) Whether to show book-count line.
 *   show_bio           (bool, default true) Whether to show bio snippet.
 *   bio_length         (int,  default 120)  Truncate bio to this many chars.
 *   show_website       (bool, default true) Whether to show website link.
 *   show_featured_first (bool, default true) Whether featured authors sort first.
 *   show_filter        (bool, default true) Whether to show the A-Z filter strip.
 *   orderby            (str,  default 'name') get_terms() orderby.
 *   order              (str,  default 'ASC')  get_terms() order direction.
 *   sort               (str,  default 'first_name') Sort authors by 'first_name'
 *                      (term name) or 'last_name' (mbs_name_key). When
 *                      'last_name' the A-Z filter and card ordering both use
 *                      the surname / key name. F-SORT-2.
 *
 * @package MyBookShowroom
 * @since   1.0.0
 */

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

class MBS_Shortcode_Author_Directory { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/**
	 * Render the author directory shortcode.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string HTML output.
	 */
	public static function render( array $atts = array() ): string {
		if ( ! mbs_is_registered() ) {
			return '';
		}

		if ( ! mbs_is_pro() ) {
			return '<!-- MBS: Author Directory requires PRO license -->';
		}

		$atts = shortcode_atts( array(
			'columns'             => 3,
			'show_count'          => true,
			'show_bio'            => true,
			'bio_length'          => 120,
			'show_website'        => true,
			'show_featured_first' => true,
			'show_filter'         => true,
			'orderby'             => 'name',
			'order'               => 'ASC',
			'sort'                => 'first_name',
		), $atts, 'mbs_author_directory' );

		// Normalise boolean-like shortcode attributes (shortcode_atts returns strings
		// for defaults that were bool — the HTML attribute 'false' must be treated
		// as the boolean false, not the truthy non-empty string).
		$show_count          = ( 'false' !== $atts['show_count'] && ! empty( $atts['show_count'] ) );
		$show_bio            = ( 'false' !== $atts['show_bio'] && ! empty( $atts['show_bio'] ) );
		$show_website        = ( 'false' !== $atts['show_website'] && ! empty( $atts['show_website'] ) );
		$show_featured_first = ( 'false' !== $atts['show_featured_first'] && ! empty( $atts['show_featured_first'] ) );
		$show_filter         = ( 'false' !== $atts['show_filter'] && ! empty( $atts['show_filter'] ) );
		$bio_length          = max( 10, absint( $atts['bio_length'] ) );
		$columns             = max( 1, min( 6, absint( $atts['columns'] ) ) );

		// F-SORT-2: Validate sort attribute.
		$sort = in_array( $atts['sort'], array( 'first_name', 'last_name' ), true )
			? $atts['sort']
			: 'first_name';

		$authors = get_terms( array(
			'taxonomy'   => MBS_TAX_AUTHOR,
			'hide_empty' => true,
			'orderby'    => sanitize_key( $atts['orderby'] ),
			'order'      => sanitize_key( $atts['order'] ),
		) );

		if ( is_wp_error( $authors ) || empty( $authors ) ) {
			return '<p>' . esc_html__( 'No authors found.', 'my-book-showroom' ) . '</p>';
		}

		// F-SORT-2: When sort=last_name, re-sort by mbs_name_key before featured split.
		if ( 'last_name' === $sort ) {
			usort( $authors, static function ( $a, $b ) {
				$ka = (string) get_term_meta( $a->term_id, 'mbs_name_key', true );
				$kb = (string) get_term_meta( $b->term_id, 'mbs_name_key', true );
				if ( '' === $ka ) {
					// F-SORT-2 fallback: use last word of display name as surname when mbs_name_key not set.
					$_parts = explode( ' ', trim( $a->name ) );
					$ka     = function_exists( 'mb_strtolower' ) ? mb_strtolower( end( $_parts ) ) : strtolower( end( $_parts ) );
				}
				if ( '' === $kb ) {
					$_parts = explode( ' ', trim( $b->name ) );
					$kb     = function_exists( 'mb_strtolower' ) ? mb_strtolower( end( $_parts ) ) : strtolower( end( $_parts ) );
				}
				return strcmp( $ka, $kb );
			} );
		}

		// ── Featured-first sort ──────────────────────────────────────────────────
		// Partition into featured / non-featured, then re-join. Each partition
		// preserves the sort ordering established above.
		if ( $show_featured_first ) {
			$featured_authors     = array();
			$non_featured_authors = array();
			foreach ( $authors as $author ) {
				if ( get_term_meta( $author->term_id, 'mbs_author_featured', true ) ) {
					$featured_authors[] = $author;
				} else {
					$non_featured_authors[] = $author;
				}
			}
			$authors = array_merge( $featured_authors, $non_featured_authors );
		}

		// ── Enqueue front-end JS for A-Z filter ─────────────────────────────────
		if ( $show_filter ) {
			wp_enqueue_script(
				'mbs-author-directory',
				plugins_url( 'assets/js/mbs-author-directory.js', MBS_DIR . 'bookshowroom.php' ),
				array(),
				MBS_VERSION,
				true
			);
		}

		// ── Build A-Z initial set for filter buttons ─────────────────────────────
		// F-SORT-2: When sort=last_name, use mbs_name_key for the initial letter.
		$initials = array();
		if ( $show_filter ) {
			foreach ( $authors as $author ) {
				if ( 'last_name' === $sort ) {
					$name_key = (string) get_term_meta( $author->term_id, 'mbs_name_key', true );
					if ( '' !== $name_key ) {
						$source = $name_key;
					} else {
						$_gp    = explode( ' ', trim( $author->name ) );
						$source = end( $_gp ) ?: $author->name;
					}
				} else {
					$source = $author->name;
				}
				$first = function_exists( 'mb_substr' )
					? mb_strtolower( mb_substr( $source, 0, 1 ) )
					: strtolower( substr( $source, 0, 1 ) );
				$initials[ $first ] = true;
			}
			ksort( $initials );
		}

		// ── Output ──────────────────────────────────────────────────────────────
		ob_start();
		echo '<div class="mbs-wrapper mbs-author-directory">';

		// A-Z filter strip.
		if ( $show_filter && ! empty( $initials ) ) {
			echo '<nav class="mbs-author-filter" aria-label="' . esc_attr__( 'Filter authors by letter', 'my-book-showroom' ) . '">';
			echo '<button class="mbs-author-filter-btn mbs-author-filter-all is-active" data-filter="all">'
				. esc_html__( 'All', 'my-book-showroom' )
				. '</button>';
			foreach ( array_keys( $initials ) as $letter ) {
				echo '<button class="mbs-author-filter-btn" data-filter="' . esc_attr( $letter ) . '">'
					. esc_html( strtoupper( $letter ) )
					. '</button>';
			}
			echo '</nav>';
		}

		// CSS custom property for column count — intentional dynamic inline style.
		echo '<div class="mbs-author-grid" style="--mbs-author-cols: ' . $columns . ';">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $columns is absint()-clamped to 1–6 at line 89; CSS custom property value is integer-safe

		foreach ( $authors as $author ) {
			$term_id   = (int) $author->term_id;
			$link      = get_term_link( $author, MBS_TAX_AUTHOR );
			$photo_id  = (int) get_term_meta( $term_id, 'mbs_author_photo_id', true );
			$photo_url = $photo_id
				? (string) wp_get_attachment_image_url( $photo_id, 'medium' ) // R5-exempt: author profile photo, not book cover
				: MBS_ASSETS_URL . 'images/placeholder.svg';
			$book_count  = (int) $author->count;
			$is_featured = (bool) get_term_meta( $term_id, 'mbs_author_featured', true );

			// data-initial attribute drives the A-Z filter JS.
			// F-SORT-2: When sort=last_name, derive initial from mbs_name_key.
			if ( 'last_name' === $sort ) {
				$_name_key = (string) get_term_meta( $term_id, 'mbs_name_key', true );
				if ( '' !== $_name_key ) {
					$_source = $_name_key;
				} else {
					$_gp2    = explode( ' ', trim( $author->name ) );
					$_source = end( $_gp2 ) ?: $author->name;
				}
			} else {
				$_source = $author->name;
			}
			$initial = function_exists( 'mb_substr' )
				? mb_strtolower( mb_substr( $_source, 0, 1 ) )
				: strtolower( substr( $_source, 0, 1 ) );
			unset( $_name_key, $_source );

			$card_classes = 'mbs-author-card';
			if ( $is_featured ) {
				$card_classes .= ' mbs-author-card--featured';
			}

			echo '<div class="' . esc_attr( $card_classes ) . '" data-initial="' . esc_attr( $initial ) . '">';

			// Photo.
			echo '<a href="' . esc_url( $link ) . '" class="mbs-author-card-photo-link" tabindex="-1" aria-hidden="true">';
			echo '<img src="' . esc_url( $photo_url ) . '" alt="" class="mbs-author-photo" loading="lazy">';
			if ( $is_featured ) {
				echo '<span class="mbs-author-featured-badge">' . esc_html__( 'Featured', 'my-book-showroom' ) . '</span>';
			}
			echo '</a>';

			// Card body.
			echo '<div class="mbs-author-card-body">';

			// Name.
			echo '<h3 class="mbs-author-card-name"><a href="' . esc_url( $link ) . '">' . esc_html( $author->name ) . '</a></h3>';

			// Book count.
			if ( $show_count ) {
				echo '<p class="mbs-author-book-count">' . esc_html( sprintf(
					/* translators: %d: number of books */
					_n( '%d book', '%d books', $book_count, 'my-book-showroom' ),
					$book_count
				) ) . '</p>';
			}

			// Bio snippet — strip HTML, truncate, append ellipsis when trimmed.
			if ( $show_bio ) {
				$bio_raw = (string) get_term_meta( $term_id, 'mbs_author_bio', true );
				$bio_plain = wp_strip_all_tags( $bio_raw );
				if ( '' !== $bio_plain ) {
					$snippet = mb_strlen( $bio_plain ) > $bio_length
						? mb_substr( $bio_plain, 0, $bio_length ) . '&hellip;'
						: $bio_plain;
					echo '<p class="mbs-author-bio-snippet">' . esc_html( $bio_plain === $snippet ? $bio_plain : mb_substr( $bio_plain, 0, $bio_length ) ) . ( mb_strlen( $bio_plain ) > $bio_length ? '&hellip;' : '' ) . '</p>';
				}
			}

			// Website link.
			if ( $show_website ) {
				$website_url = (string) get_term_meta( $term_id, 'mbs_author_website_url', true );
				if ( '' !== $website_url ) {
					echo '<a class="mbs-author-website-link" href="' . esc_url( $website_url ) . '" target="_blank" rel="noopener noreferrer">'
						. esc_html__( 'Website', 'my-book-showroom' )
						. '</a>';
				}
			}

			echo '</div>'; // .mbs-author-card-body
			echo '</div>'; // .mbs-author-card
		}

		echo '</div>'; // .mbs-author-grid
		echo '</div>'; // .mbs-wrapper

		return ob_get_clean();
	}
}
