<?php
/**
 * XML Sitemap generation.
 *
 * @package Dennis_SEO
 */

defined( 'ABSPATH' ) || exit;

class Dennis_SEO_Sitemap {

	public function __construct() {
		add_action( 'init', array( __CLASS__, 'register_rewrite_rules' ) );
		add_action( 'template_redirect', array( $this, 'serve_sitemap' ) );
		add_action( 'save_post', array( $this, 'bust_cache' ) );
		add_action( 'delete_post', array( $this, 'bust_cache' ) );

		// Redirect old Yoast sitemap URL.
		add_action( 'template_redirect', array( $this, 'redirect_yoast_sitemap' ), 1 );
	}

	public static function register_rewrite_rules(): void {
		add_rewrite_rule( '^sitemap\.xml$', 'index.php?dennis_sitemap=index', 'top' );
		add_rewrite_rule( '^sitemap-([a-z_]+)\.xml$', 'index.php?dennis_sitemap=$matches[1]', 'top' );
		add_filter( 'query_vars', function ( $vars ) {
			$vars[] = 'dennis_sitemap';
			return $vars;
		} );
	}

	/**
	 * Redirect Yoast's sitemap URL to ours.
	 */
	public function redirect_yoast_sitemap(): void {
		if ( isset( $_SERVER['REQUEST_URI'] ) ) {
			$path = wp_parse_url( sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH );
			if ( '/sitemap_index.xml' === $path ) {
				wp_safe_redirect( home_url( '/sitemap.xml' ), 301 );
				exit;
			}
		}
	}

	public function serve_sitemap(): void {
		$type = get_query_var( 'dennis_sitemap' );
		if ( ! $type ) {
			return;
		}

		status_header( 200 );
		header( 'Content-Type: application/xml; charset=UTF-8' );
		header( 'X-Robots-Tag: noindex' );

		if ( 'index' === $type ) {
			echo $this->generate_index(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		} else {
			echo $this->generate_post_type_sitemap( $type ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
		exit;
	}

	private function generate_index(): string {
		$xml  = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
		$xml .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";

		// Auto-discover all public post types.
		$post_types = get_post_types( array( 'public' => true ), 'names' );
		// Ensure page and post come first, then alphabetical.
		$ordered = array();
		if ( isset( $post_types['page'] ) ) {
			$ordered[] = 'page';
		}
		if ( isset( $post_types['post'] ) ) {
			$ordered[] = 'post';
		}
		foreach ( $post_types as $pt ) {
			if ( ! in_array( $pt, array( 'page', 'post', 'attachment' ), true ) ) {
				$ordered[] = $pt;
			}
		}
		$post_types = $ordered;
		foreach ( $post_types as $pt ) {
			$count = wp_count_posts( $pt );
			if ( isset( $count->publish ) && $count->publish > 0 ) {
				$xml .= '  <sitemap>' . "\n";
				$xml .= '    <loc>' . esc_url( home_url( "/sitemap-{$pt}.xml" ) ) . '</loc>' . "\n";
				$xml .= '  </sitemap>' . "\n";
			}
		}

		$xml .= '</sitemapindex>';
		return $xml;
	}

	private function generate_post_type_sitemap( string $post_type ): string {
		// Validate post type exists and is public.
		if ( ! post_type_exists( $post_type ) || ! is_post_type_viewable( $post_type ) ) {
			return '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>';
		}

		// Check cache.
		$cache_key = 'dennis_seo_sitemap_' . $post_type;
		$cached    = get_transient( $cache_key );
		if ( false !== $cached ) {
			return $cached;
		}

		$args = array(
			'post_type'      => $post_type,
			'post_status'    => 'publish',
			'posts_per_page' => 2000,
			'orderby'        => 'modified',
			'order'          => 'DESC',
			'meta_query'     => array(
				'relation' => 'OR',
				array(
					'key'     => '_dennis_seo_robots_noindex',
					'value'   => '1',
					'compare' => '!=',
				),
				array(
					'key'     => '_dennis_seo_robots_noindex',
					'compare' => 'NOT EXISTS',
				),
			),
		);

		// Exclude the static front page to avoid duplicate with the manual homepage entry.
		$front_page_id = (int) get_option( 'page_on_front' );
		if ( $front_page_id && 'page' === $post_type ) {
			$args['post__not_in'] = array( $front_page_id );
		}

		$posts = get_posts( $args );

		$xml  = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
		$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";

		// Add homepage to page sitemap.
		if ( 'page' === $post_type ) {
			$xml .= '  <url>' . "\n";
			$xml .= '    <loc>' . esc_url( home_url( '/' ) ) . '</loc>' . "\n";
			$xml .= '    <lastmod>' . esc_html( gmdate( 'Y-m-d\TH:i:s+00:00' ) ) . '</lastmod>' . "\n";
			$xml .= '    <changefreq>weekly</changefreq>' . "\n";
			$xml .= '    <priority>1.0</priority>' . "\n";
			$xml .= '  </url>' . "\n";
		}

		foreach ( $posts as $post ) {
			$url     = get_permalink( $post );
			$lastmod = get_the_modified_date( 'Y-m-d\TH:i:s+00:00', $post );

			$xml .= '  <url>' . "\n";
			$xml .= '    <loc>' . esc_url( $url ) . '</loc>' . "\n";
			if ( $lastmod ) {
				$xml .= '    <lastmod>' . esc_html( $lastmod ) . '</lastmod>' . "\n";
			}
			$xml .= '  </url>' . "\n";
		}

		$xml .= '</urlset>';

		// Cache for 1 hour.
		set_transient( $cache_key, $xml, HOUR_IN_SECONDS );

		return $xml;
	}

	public function bust_cache(): void {
		delete_transient( 'dennis_seo_sitemap_post' );
		delete_transient( 'dennis_seo_sitemap_page' );
	}
}
