<?php
/**
 * Sitemap analyzer.
 *
 * Checks XML sitemap availability and configuration.
 *
 * @package MonitorCat\Modules\Seo\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Seo\Analyzer;

defined( 'ABSPATH' ) || exit;

/**
 * Analyzes XML sitemap presence and accessibility.
 */
final class SitemapAnalyzer {

	/**
	 * Run the sitemap analysis on a post.
	 *
	 * @param int $post_id The post ID to analyze (used for context, sitemap is site-wide).
	 * @return array{score: int, status: string, checks: array<int, array{label: string, status: string, message: string, value: string|null}>}
	 */
	public function analyze( int $post_id ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- shared analyzer interface.
		$checks = array();

		$checks[] = $this->check_wp_native_sitemap();
		$checks[] = $this->check_yoast_sitemap();
		$checks[] = $this->check_robots_sitemap_directive();

		$score  = $this->calculate_score( $checks );
		$status = $this->determine_status( $score );

		return array(
			'score'  => $score,
			'status' => $status,
			'checks' => $checks,
		);
	}

	/**
	 * Check if WordPress native sitemap is accessible (wp-sitemap.xml, WP 5.5+).
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_wp_native_sitemap(): array {
		$sitemap_url = site_url( '/wp-sitemap.xml' );
		$response    = wp_remote_head( $sitemap_url, array( 'timeout' => 5 ) );

		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'WordPress Sitemap', 'monitor-cat' ),
				'status'  => 'warning',
				/* translators: %s: error message */
				'message' => sprintf( __( 'Could not check WordPress sitemap: %s', 'monitor-cat' ), $response->get_error_message() ),
				'value'   => null,
			);
		}

		$status_code = wp_remote_retrieve_response_code( $response );

		if ( 200 === $status_code ) {
			return array(
				'label'   => __( 'WordPress Sitemap', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'WordPress native sitemap (wp-sitemap.xml) is accessible.', 'monitor-cat' ),
				'value'   => $sitemap_url,
			);
		}

		return array(
			'label'   => __( 'WordPress Sitemap', 'monitor-cat' ),
			'status'  => 'warning',
			/* translators: %d: HTTP status code */
			'message' => sprintf( __( 'WordPress native sitemap returned HTTP %d. It may be disabled by a plugin.', 'monitor-cat' ), $status_code ),
			'value'   => (string) $status_code,
		);
	}

	/**
	 * Check if Yoast SEO sitemap is accessible.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_yoast_sitemap(): array {
		if ( ! defined( 'WPSEO_VERSION' ) ) {
			return array(
				'label'   => __( 'Yoast Sitemap', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'Yoast SEO is not active. Skipping Yoast sitemap check.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$sitemap_url = site_url( '/sitemap_index.xml' );
		$response    = wp_remote_head( $sitemap_url, array( 'timeout' => 5 ) );

		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'Yoast Sitemap', 'monitor-cat' ),
				'status'  => 'warning',
				/* translators: %s: error message */
				'message' => sprintf( __( 'Could not check Yoast sitemap: %s', 'monitor-cat' ), $response->get_error_message() ),
				'value'   => null,
			);
		}

		$status_code = wp_remote_retrieve_response_code( $response );

		if ( 200 === $status_code ) {
			return array(
				'label'   => __( 'Yoast Sitemap', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'Yoast SEO sitemap (sitemap_index.xml) is accessible.', 'monitor-cat' ),
				'value'   => $sitemap_url,
			);
		}

		return array(
			'label'   => __( 'Yoast Sitemap', 'monitor-cat' ),
			'status'  => 'fail',
			/* translators: %d: HTTP status code */
			'message' => sprintf( __( 'Yoast sitemap returned HTTP %d. Check your Yoast SEO settings.', 'monitor-cat' ), $status_code ),
			'value'   => (string) $status_code,
		);
	}

	/**
	 * Check robots.txt for a sitemap directive.
	 *
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_robots_sitemap_directive(): array {
		$robots_url = site_url( '/robots.txt' );
		$response   = wp_remote_get( $robots_url, array( 'timeout' => 5 ) );

		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'Robots.txt Sitemap', 'monitor-cat' ),
				'status'  => 'warning',
				/* translators: %s: error message */
				'message' => sprintf( __( 'Could not check robots.txt: %s', 'monitor-cat' ), $response->get_error_message() ),
				'value'   => null,
			);
		}

		$body = wp_remote_retrieve_body( $response );

		if ( preg_match( '/^Sitemap:\s*(.+)$/im', $body, $match ) ) {
			return array(
				'label'   => __( 'Robots.txt Sitemap', 'monitor-cat' ),
				'status'  => 'pass',
				/* translators: %s: sitemap URL */
				'message' => sprintf( __( 'Sitemap directive found in robots.txt: %s', 'monitor-cat' ), trim( $match[1] ) ),
				'value'   => trim( $match[1] ),
			);
		}

		return array(
			'label'   => __( 'Robots.txt Sitemap', 'monitor-cat' ),
			'status'  => 'warning',
			'message' => __( 'No Sitemap directive found in robots.txt. Consider adding one.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Calculate weighted score from checks.
	 *
	 * @param array<int, array{label: string, status: string, message: string, value: string|null}> $checks The checks array.
	 * @return int Score from 0 to 100.
	 */
	private function calculate_score( array $checks ): int {
		if ( empty( $checks ) ) {
			return 0;
		}

		$weights = array( 40, 30, 30 );
		$total   = 0;

		foreach ( $checks as $index => $check ) {
			$weight     = $weights[ $index ] ?? 30;
			$multiplier = 'pass' === $check['status'] ? 1.0 : ( 'warning' === $check['status'] ? 0.5 : 0.0 );
			$total     += $weight * $multiplier;
		}

		return (int) round( $total );
	}

	/**
	 * Determine overall status from score.
	 *
	 * @param int $score The calculated score.
	 * @return string 'pass', 'warning', or 'fail'.
	 */
	private function determine_status( int $score ): string {
		if ( $score >= 80 ) {
			return 'pass';
		}

		if ( $score >= 50 ) {
			return 'warning';
		}

		return 'fail';
	}
}
