<?php
/**
 * JSON-LD Schema.org structured data class.
 *
 * Outputs inline <script type="application/ld+json"> on relevant pages.
 * Skips output when a dedicated SEO plugin is active.
 *
 * @package CyyWordpress
 */

if ( ! class_exists( 'CyyWordpress_Schema' ) ) :

	/**
	 * Handles JSON-LD output.
	 */
	class CyyWordpress_Schema {

		/**
		 * Register hooks.
		 */
		public static function init(): void {
			// Bail early if a popular SEO plugin already handles schema.
			if (
				defined( 'WPSEO_VERSION' )
				|| defined( 'RANKMATH_VERSION' )
				|| class_exists( 'All_in_One_SEO_Pack' )
			) {
				return;
			}

			add_action( 'wp_head', array( static::class, 'output_schema' ) );
		}

		/**
		 * Determine page type and output the correct schema.
		 */
		public static function output_schema(): void {
			$schema = null;

			if ( is_front_page() || is_home() ) {
				$schema = self::website_schema();
			} elseif ( is_singular( 'post' ) ) {
				$schema = self::article_schema();
			} elseif ( is_author() ) {
				$schema = self::person_schema();
			}

			if ( $schema ) {
				// json_encode with JSON_UNESCAPED_UNICODE for readable Chinese output.
				echo '<script type="application/ld+json">'
				     . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT )
				     . '</script>' . PHP_EOL;
			}
		}

		/**
		 * WebSite schema with SearchAction for Sitelink Search Box.
		 *
		 * @return array<string, mixed>
		 */
		private static function website_schema(): array {
			return array(
				'@context' => 'https://schema.org',
				'@type'    => 'WebSite',
				'name'     => get_bloginfo( 'name' ),
				'url'      => home_url( '/' ),
				'potentialAction' => array(
					'@type'       => 'SearchAction',
					'target'      => array(
						'@type'       => 'EntryPoint',
						'urlTemplate' => home_url( '/?s={search_term_string}' ),
					),
					'query-input' => 'required name=search_term_string',
				),
			);
		}

		/**
		 * Article schema for single posts.
		 *
		 * @return array<string, mixed>
		 */
		private static function article_schema(): array {
			global $post;

			$author_id    = (int) get_the_author_meta( 'ID' );
			$author_name  = get_the_author_meta( 'display_name', $author_id );
			$author_url   = get_author_posts_url( $author_id );
			$image_url    = '';

			if ( has_post_thumbnail() ) {
				$src = wp_get_attachment_image_src( (int) get_post_thumbnail_id(), 'large' );
				if ( $src ) {
					$image_url = $src[0];
				}
			}

			$schema = array(
				'@context'      => 'https://schema.org',
				'@type'         => 'Article',
				'headline'      => get_the_title(),
				'url'           => (string) get_permalink(),
				'datePublished' => get_the_date( DATE_W3C ),
				'dateModified'  => get_the_modified_date( DATE_W3C ),
				'author'        => array(
					'@type' => 'Person',
					'name'  => $author_name,
					'url'   => $author_url,
				),
				'publisher'     => array(
					'@type' => 'Organization',
					'name'  => get_bloginfo( 'name' ),
					'url'   => home_url( '/' ),
				),
				'description'   => has_excerpt()
					? get_the_excerpt()
					: wp_trim_words( wp_strip_all_tags( $post->post_content ), 30 ),
			);

			if ( $image_url ) {
				$schema['image'] = $image_url;
			}

			return $schema;
		}

		/**
		 * Person schema for author archive pages.
		 *
		 * @return array<string, mixed>
		 */
		private static function person_schema(): array {
			$author      = get_queried_object();
			$author_id   = ( $author instanceof WP_User ) ? $author->ID : 0;
			$author_name = get_the_author_meta( 'display_name', $author_id );
			$author_url  = get_author_posts_url( $author_id );
			$bio         = get_the_author_meta( 'description', $author_id );

			$schema = array(
				'@context' => 'https://schema.org',
				'@type'    => 'Person',
				'name'     => $author_name,
				'url'      => $author_url,
			);

			if ( $bio ) {
				$schema['description'] = $bio;
			}

			return $schema;
		}
	}

endif;
