<?php
/**
 * llms.txt analyzer.
 *
 * Checks if the site has a valid /llms.txt file — the "robots.txt for AI"
 * that helps large language models understand site structure.
 *
 * @package MonitorCat\Modules\Seo\Analyzer
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Seo\Analyzer;

defined( 'ABSPATH' ) || exit;

/**
 * Analyzes the presence and quality of /llms.txt.
 */
final class LlmsTxtAnalyzer {

	/**
	 * Run the llms.txt analysis.
	 *
	 * @param int $post_id The post ID (unused — this is a site-wide check).
	 * @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.
		$llms_url = site_url( '/llms.txt' );
		$response = wp_remote_get( $llms_url, array( 'timeout' => 5 ) );
		$checks   = array();

		$checks[] = $this->check_exists( $response );

		$body = '';
		if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) {
			$body = wp_remote_retrieve_body( $response );
		}

		$checks[] = $this->check_format( $body );
		$checks[] = $this->check_coverage( $body );

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

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

	/**
	 * Check if /llms.txt exists and returns HTTP 200.
	 *
	 * @param array<string, mixed>|\WP_Error $response The HTTP response.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_exists( $response ): array {
		if ( is_wp_error( $response ) ) {
			return array(
				'label'   => __( 'llms.txt Exists', 'monitor-cat' ),
				'status'  => 'fail',
				'message' => __( 'Could not reach /llms.txt. This file helps AI engines understand your site structure.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$status_code = wp_remote_retrieve_response_code( $response );

		if ( 200 === $status_code ) {
			return array(
				'label'   => __( 'llms.txt Exists', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( '/llms.txt is accessible. AI engines can use this to understand your site.', 'monitor-cat' ),
				'value'   => __( 'Found', 'monitor-cat' ),
			);
		}

		return array(
			'label'   => __( 'llms.txt Exists', 'monitor-cat' ),
			'status'  => 'fail',
			/* translators: %d: HTTP status code */
			'message' => sprintf( __( '/llms.txt returned HTTP %d. Enable auto-generation in Monitor.Cat SEO settings or create this file manually.', 'monitor-cat' ), $status_code ),
			'value'   => null,
		);
	}

	/**
	 * Check if the llms.txt content is valid Markdown with links and headings.
	 *
	 * @param string $body The llms.txt content.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_format( string $body ): array {
		if ( empty( $body ) ) {
			return array(
				'label'   => __( 'Content Format', 'monitor-cat' ),
				'status'  => 'fail',
				'message' => __( 'llms.txt is empty or missing.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		$has_headings = (bool) preg_match( '/^#+\s+/m', $body );
		$has_links    = (bool) preg_match( '/\[.+?\]\(.+?\)/', $body ) || (bool) preg_match( '/https?:\/\//', $body );

		if ( $has_headings && $has_links ) {
			return array(
				'label'   => __( 'Content Format', 'monitor-cat' ),
				'status'  => 'pass',
				'message' => __( 'llms.txt contains properly formatted Markdown with headings and links.', 'monitor-cat' ),
				'value'   => __( 'Valid Markdown', 'monitor-cat' ),
			);
		}

		if ( $has_headings || $has_links ) {
			return array(
				'label'   => __( 'Content Format', 'monitor-cat' ),
				'status'  => 'warning',
				'message' => __( 'llms.txt is partially formatted. Include both Markdown headings (# Section) and links for best results.', 'monitor-cat' ),
				'value'   => null,
			);
		}

		return array(
			'label'   => __( 'Content Format', 'monitor-cat' ),
			'status'  => 'fail',
			'message' => __( 'llms.txt does not contain valid Markdown structure. It should include headings and links to key pages.', 'monitor-cat' ),
			'value'   => null,
		);
	}

	/**
	 * Check if llms.txt references enough key pages.
	 *
	 * @param string $body The llms.txt content.
	 * @return array{label: string, status: string, message: string, value: string|null}
	 */
	private function check_coverage( string $body ): array {
		if ( empty( $body ) ) {
			return array(
				'label'   => __( 'Content Coverage', 'monitor-cat' ),
				'status'  => 'fail',
				'message' => __( 'No content to evaluate coverage.', 'monitor-cat' ),
				'value'   => '0',
			);
		}

		// Count unique links — collect all URLs into one set to avoid
		// double-counting a URL that appears as both Markdown and bare.
		$all_urls = array();
		if ( preg_match_all( '/\[.+?\]\((.+?)\)/', $body, $md_matches ) ) {
			foreach ( $md_matches[1] as $url ) {
				$all_urls[ $url ] = true;
			}
		}
		if ( preg_match_all( '/(?<!\()https?:\/\/[^\s)]+/', $body, $url_matches ) ) {
			foreach ( $url_matches[0] as $url ) {
				$all_urls[ $url ] = true;
			}
		}
		$link_count = count( $all_urls );

		if ( $link_count >= 3 ) {
			return array(
				'label'   => __( 'Content Coverage', 'monitor-cat' ),
				'status'  => 'pass',
				/* translators: %d: number of links */
				'message' => sprintf( __( 'llms.txt references %d pages — good coverage for AI engines.', 'monitor-cat' ), $link_count ),
				'value'   => (string) $link_count,
			);
		}

		if ( $link_count >= 1 ) {
			return array(
				'label'   => __( 'Content Coverage', 'monitor-cat' ),
				'status'  => 'warning',
				/* translators: %d: number of links */
				'message' => sprintf( __( 'llms.txt only references %d page(s). Include at least 3 key pages for adequate coverage.', 'monitor-cat' ), $link_count ),
				'value'   => (string) $link_count,
			);
		}

		return array(
			'label'   => __( 'Content Coverage', 'monitor-cat' ),
			'status'  => 'fail',
			'message' => __( 'llms.txt contains no links to site pages.', 'monitor-cat' ),
			'value'   => '0',
		);
	}

	/**
	 * 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';
	}
}
