<?php
/**
 * Front-end rendering: works list -> HTML, with caching and a last-good fallback.
 */

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

/**
 * Render callback for both the shortcode and the block.
 */
function publiton_render_publications( $atts = array() ) {
	$settings = publiton_get_settings();
	if ( '' === $settings['author_id'] ) {
		if ( current_user_can( 'manage_options' ) ) {
			return '<p><em>' . sprintf(
				/* translators: %s: settings page name */
				esc_html__( 'Publiton: choose an author under %s first.', 'publiton-auto-publication-updater' ),
				'<a href="' . esc_url( admin_url( 'options-general.php?page=publiton-auto-publication-updater' ) ) . '">' . esc_html__( 'Settings → Publications', 'publiton-auto-publication-updater' ) . '</a>'
			) . '</em></p>';
		}
		return '';
	}

	wp_enqueue_style( 'publiton-publications' );

	$pro       = publiton_can_use_pro();
	$cache_key = publiton_cache_key( $pro );

	$html = get_transient( $cache_key );
	if ( false === $html ) {
		$html = publiton_build_html( $settings, $pro );
		if ( false === $html ) {
			// API unavailable: fall back to the last known copy, if any.
			$html = get_option( PUBLITON_LAST_GOOD_OPTION, '' );
			if ( '' === $html ) {
				return '<p>' . esc_html__( 'The publication list is temporarily unavailable.', 'publiton-auto-publication-updater' ) . '</p>';
			}
			set_transient( $cache_key, $html, HOUR_IN_SECONDS ); // retry sooner
			return $html;
		}
		update_option( PUBLITON_LAST_GOOD_OPTION, $html, false );
		set_transient( $cache_key, $html, DAY_IN_SECONDS );
	}
	return $html;
}

/**
 * Cache key for the rendered list. Free and Pro output are cached separately so
 * a licence change (or its absence) never serves the wrong variant.
 */
function publiton_cache_key( $pro ) {
	return PUBLITON_CACHE_KEY . ( $pro ? '_pro' : '' );
}

/**
 * Delete every rendered-list cache variant.
 */
function publiton_clear_cache() {
	delete_transient( publiton_cache_key( false ) );
	delete_transient( publiton_cache_key( true ) );
}

function publiton_build_html( $settings, $pro = false ) {
	$works = publiton_fetch_works( $settings, $pro );
	if ( false === $works ) {
		return false;
	}

	$pro_stats = $pro && ! empty( $settings['show_stats'] );
	$pro_chart = $pro && ! empty( $settings['show_chart'] );
	$pro_expo  = $pro && ! empty( $settings['enable_export'] );

	$group_before = (int) $settings['group_before'];
	$by_year      = array();
	$archived     = array(); // everything before group_before, in one section
	foreach ( $works as $work ) {
		if ( $group_before > 0 && $work['year'] < $group_before ) {
			$archived[] = $work;
		} else {
			$by_year[ $work['year'] ][] = $work;
		}
	}
	krsort( $by_year );

	$by_date_desc = function ( $a, $b ) {
		return strcmp( $b['date'], $a['date'] );
	};
	foreach ( $by_year as &$year_works_ref ) {
		usort( $year_works_ref, $by_date_desc );
	}
	unset( $year_works_ref );
	usort( $archived, $by_date_desc );

	$current_year = (int) gmdate( 'Y' );
	$open_recent  = max( 0, (int) $settings['open_recent'] );
	$highlight    = ! empty( $settings['highlight_author'] );

	$html = '<div class="publiton-publications">';
	if ( $pro_stats ) {
		$html .= publiton_render_stats_summary( $works );
	}
	if ( $pro_chart ) {
		$html .= publiton_render_citations_chart( $works );
	}
	foreach ( $by_year as $year => $year_works ) {
		$open  = ( $year > $current_year - $open_recent );
		$html .= publiton_render_section( (string) (int) $year, $year_works, $open, $settings, $pro );
	}
	if ( ! empty( $archived ) ) {
		/* translators: %d: year before which publications are grouped together */
		$label = sprintf( __( 'Before %d', 'publiton-auto-publication-updater' ), $group_before );
		$html .= publiton_render_section( $label, $archived, false, $settings, $pro );
	}
	if ( $pro_expo ) {
		$html .= publiton_render_export_links();
	}
	$html .= '</div>';
	return $html;
}

function publiton_render_section( $label, $works, $open, $settings, $pro = false ) {
	$show_citations = $pro && ! empty( $settings['show_citations'] );
	$count = count( $works );
	/* translators: %d: number of publications in this section */
	$count_label = sprintf( _n( '%d publication', '%d publications', $count, 'publiton-auto-publication-updater' ), $count );

	$html = '<details class="publiton-year"' . ( $open ? ' open' : '' ) . '><summary>' . esc_html( $label )
		. ' <span class="publiton-count">(' . esc_html( $count_label ) . ')</span></summary><ul>';
	foreach ( $works as $work ) {
		$html .= '<li>';
		if ( $work['link'] ) {
			$html .= '<a class="publiton-title" href="' . esc_url( $work['link'] ) . '" target="_blank" rel="noopener">'
				. esc_html( $work['title'] ) . '</a>';
		} else {
			$html .= '<span class="publiton-title">' . esc_html( $work['title'] ) . '</span>';
		}
		if ( $show_citations && isset( $work['citations'] ) && $work['citations'] > 0 ) {
			$html .= ' <span class="publiton-cited">' . sprintf(
				/* translators: %s: number of citations */
				esc_html__( 'Cited by %s', 'publiton-auto-publication-updater' ),
				esc_html( number_format_i18n( (int) $work['citations'] ) )
			) . '</span>';
		}
		$meta = '';
		if ( ! empty( $settings['show_authors'] ) ) {
			$meta = publiton_render_authors( $work['authors'], $settings );
		}
		if ( ! empty( $settings['show_venue'] ) && $work['venue'] ) {
			$meta .= ( $meta ? ' &mdash; ' : '' ) . '<em>' . esc_html( $work['venue'] ) . '</em>';
		}
		if ( ! empty( $settings['show_date'] ) && ! empty( $work['date'] ) ) {
			$timestamp = strtotime( $work['date'] );
			if ( $timestamp ) {
				$meta .= ( $meta ? ' &mdash; ' : '' ) . '<span class="publiton-date">' . esc_html( date_i18n( get_option( 'date_format' ), $timestamp ) ) . '</span>';
			}
		}
		if ( $meta ) {
			// A div (not a span) so the meta line breaks even if the stylesheet fails to load.
			$html .= '<div class="publiton-meta">' . $meta . '</div>';
		}
		$html .= '</li>';
	}
	return $html . '</ul></details>';
}

function publiton_render_authors( $authors, $settings ) {
	if ( empty( $authors ) ) {
		return '';
	}
	$highlight   = ! empty( $settings['highlight_author'] );
	$max_authors = max( 0, (int) $settings['max_authors'] );
	if ( $max_authors > 0 && count( $authors ) > $max_authors ) {
		$authors   = array_slice( $authors, 0, $max_authors );
		$authors[] = array(
			'name'    => __( 'et al.', 'publiton-auto-publication-updater' ),
			'you'     => false,
			'literal' => true,
		);
	}
	$initials = ( 'initials' === $settings['author_format'] );
	$out      = array();
	foreach ( $authors as $author ) {
		$name = ( $initials && empty( $author['literal'] ) ) ? publiton_abbreviate_name( $author['name'] ) : $author['name'];
		$escaped = esc_html( $name );
		if ( $highlight && $author['you'] ) {
			$escaped = '<strong>' . $escaped . '</strong>';
		}
		$out[] = $escaped;
	}
	return implode( ', ', $out );
}

/**
 * "Wijnand A. IJsselsteijn" -> "W. A. IJsselsteijn"; keeps lowercase name
 * particles ("Roy van den Heuvel" -> "R. van den Heuvel") and the last word.
 */
function publiton_abbreviate_name( $name ) {
	$particles = array( 'van', 'de', 'den', 'der', 'ten', 'ter', 'te', 'von', 'zu', 'la', 'le', 'lo', 'di', 'da', 'del', 'della', 'dos', 'du', 'el', 'al', "van't", "'t" );
	$words     = preg_split( '/\s+/', trim( $name ) );
	$last      = count( $words ) - 1;
	// Find where the surname starts: the last run of particles + final word.
	$surname_start = $last;
	while ( $surname_start > 0 && in_array( mb_strtolower( $words[ $surname_start - 1 ] ), $particles, true ) ) {
		$surname_start--;
	}
	$out = array();
	foreach ( $words as $i => $word ) {
		if ( $i >= $surname_start || '' === $word ) {
			$out[] = $word;
		} else {
			$first = mb_substr( $word, 0, 1 );
			// Keep hyphenated initials readable: "Jean-Pierre" -> "J.-P."
			if ( false !== mb_strpos( $word, '-' ) ) {
				$parts = array_map(
					function ( $part ) {
						return '' === $part ? '' : mb_substr( $part, 0, 1 ) . '.';
					},
					explode( '-', $word )
				);
				$out[] = implode( '-', $parts );
			} else {
				$out[] = $first . '.';
			}
		}
	}
	return implode( ' ', $out );
}
