<?php
/**
 * SEO Analyzer — site-wide SEO tests
 *
 * @package SEOvaultSEO
 */

namespace SEOvaultSEO\SeoAnalyzer;

use SEOvaultSEO\Core\PostMeta;

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

class SeoAnalyzer {

	/**
	 * Instance.
	 *
	 * @var SeoAnalyzer|null
	 */
	private static $instance = null;

	/**
	 * Registered tests.
	 *
	 * @var array
	 */
	private $tests = array();

	/**
	 * Results from the last run.
	 *
	 * @var array|null
	 */
	public $results = null;

	/**
	 * Timestamp of the last run.
	 *
	 * @var int|null
	 */
	public $results_date = null;

	/**
	 * Option key prefix for storage.
	 */
	const OPTION_RESULTS = 'seovaultseo_analyzer_results';
	const OPTION_DATE    = 'seovaultseo_analyzer_date';

	/**
	 * Get singleton.
	 *
	 * @return SeoAnalyzer
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor — registers tests.
	 */
	private function __construct() {
		$this->register_tests();
	}

	/**
	 * Register all built-in tests.
	 *
	 * Each test: key => [
	 *   'title', 'description', 'how_to_fix', 'category', 'callback', 'score'
	 * ]
	 */
	private function register_tests() {
		$tests = array();

		// — Priority —

		$tests['blog_public'] = array(
			'title'       => __( 'Search Engine Visibility', 'seovault-ai' ),
			'description' => __( 'Your site must be visible to search engines to appear in search results.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Navigate to Settings > Reading and ensure "Discourage search engines from indexing this site" is unchecked.', 'seovault-ai' ) . '</p>',
			'category'    => 'priority',
			'callback'    => array( Tests::class, 'test_blog_public' ),
			'score'       => 7,
		);

		$tests['permalink_structure'] = array(
			'title'       => __( 'Permalink Structure', 'seovault-ai' ),
			'description' => __( 'SEO-friendly permalinks include the post name in the URL for better rankings.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Go to Settings > Permalinks and select the "Post name" option for SEO-friendly URLs.', 'seovault-ai' ) . '</p>',
			'category'    => 'priority',
			'callback'    => array( Tests::class, 'test_permalink_structure' ),
			'score'       => 7,
		);

		// — Basic —

		$tests['site_tagline'] = array(
			'title'       => __( 'Site Tagline', 'seovault-ai' ),
			'description' => __( 'A custom tagline improves SEO and appears in search results and browser tabs.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Go to Settings > General and set a unique, descriptive tagline that includes your main keyword.', 'seovault-ai' ) . '</p>',
			'category'    => 'basic',
			'callback'    => array( Tests::class, 'test_site_tagline' ),
			'score'       => 5,
		);

		$tests['focus_keywords'] = array(
			'title'       => __( 'Focus Keywords', 'seovault-ai' ),
			'description' => __( 'Posts with focus keywords allow SEOvault AI to analyze and optimize content effectively.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Edit each post and set a focus keyword in the SEOvault AI meta box to enable content analysis.', 'seovault-ai' ) . '</p>',
			'category'    => 'basic',
			'callback'    => array( Tests::class, 'test_focus_keywords' ),
			'score'       => 3,
		);

		$tests['post_titles'] = array(
			'title'       => __( 'Focus Keyword in Titles', 'seovault-ai' ),
			'description' => __( 'Posts should include the focus keyword in the title for better search rankings.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Edit the posts listed below and add the focus keyword to the post title.', 'seovault-ai' ) . '</p>',
			'category'    => 'basic',
			'callback'    => array( Tests::class, 'test_post_titles' ),
			'score'       => 4,
		);

		$tests['title_templates'] = array(
			'title'       => __( 'Title & Meta Description Templates', 'seovault-ai' ),
			'description' => __( 'Configured templates ensure consistent SEO titles and descriptions across all posts.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Go to SEOvault AI > General > Templates and configure your title and description templates.', 'seovault-ai' ) . '</p>',
			'category'    => 'basic',
			'callback'    => array( Tests::class, 'test_title_templates' ),
			'score'       => 4,
		);

		$tests['homepage_seo'] = array(
			'title'       => __( 'Static Homepage SEO', 'seovault-ai' ),
			'description' => __( 'A static homepage needs page-specific SEO metadata and a healthy page assessment.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'If Settings > Reading uses a static homepage, edit that page and complete its SEOvault AI title, description, keyword, indexing, and canonical settings.', 'seovault-ai' ) . '</p>',
			'category'    => 'basic',
			'callback'    => array( Tests::class, 'test_homepage_seo' ),
			'score'       => 5,
		);

		// — Advanced SEO —

		$tests['sitemap'] = array(
			'title'       => __( 'XML Sitemap', 'seovault-ai' ),
			'description' => __( 'An XML sitemap helps search engines discover and crawl all pages on your site.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Ensure the sitemap is enabled in SEOvault AI settings. Verify the sitemap is accessible at your-site/sitemap.xml', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_sitemap' ),
			'score'       => 3,
		);

		$tests['robots_txt'] = array(
			'title'       => __( 'robots.txt', 'seovault-ai' ),
			'description' => __( 'A robots.txt file guides search engine crawlers on which pages to crawl and index.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Create or verify a robots.txt file in your site root. Ensure it does not block important pages from being crawled.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_robots_txt' ),
			'score'       => 3,
		);

		$tests['ssl'] = array(
			'title'       => __( 'HTTPS / SSL', 'seovault-ai' ),
			'description' => __( 'HTTPS encrypts traffic and is a confirmed Google ranking signal.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Install an SSL certificate and force HTTPS via Settings > General. Update your site URLs to use https://', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_ssl' ),
			'score'       => 7,
		);

		$tests['schema'] = array(
			'title'       => __( 'Schema Markup', 'seovault-ai' ),
			'description' => __( 'Structured data (schema.org) helps search engines understand your content and can enable rich results.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Enable schema types in SEOvault AI > Schema. At minimum, enable Organization or Website schema.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_schema' ),
			'score'       => 3,
		);

		$tests['open_graph'] = array(
			'title'       => __( 'Open Graph Tags', 'seovault-ai' ),
			'description' => __( 'Open Graph tags control how your content appears when shared on social media platforms.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Ensure Open Graph output is enabled. Set a default social image in SEOvault AI > General > Social.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_open_graph' ),
			'score'       => 2,
		);

		$tests['canonical'] = array(
			'title'       => __( 'Canonical URLs', 'seovault-ai' ),
			'description' => __( 'Canonical tags prevent duplicate content issues by specifying the preferred URL for each page.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Ensure SEOvault AI is in Auto output mode so canonical URLs are automatically generated.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_canonical' ),
			'score'       => 5,
		);

		$tests['rss_feeds'] = array(
			'title'       => __( 'RSS Feeds', 'seovault-ai' ),
			'description' => __( 'RSS feeds allow users and services to subscribe to your content updates.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Ensure RSS feeds are not disabled. WordPress generates feeds automatically — check Settings > Reading.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_rss_feeds' ),
			'score'       => 2,
		);

		$tests['response_time'] = array(
			'title'       => __( 'Server Response Time', 'seovault-ai' ),
			'description' => __( 'Fast server response times improve user experience and are a Core Web Vitals metric.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Use a caching plugin, optimize your database, and consider a CDN. Aim for a TTFB under 600ms.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_response_time' ),
			'score'       => 3,
		);

		$tests['h1_heading'] = array(
			'title'       => __( 'H1 Heading on Homepage', 'seovault-ai' ),
			'description' => __( 'Every page should have exactly one H1 heading that describes the page content.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Ensure your homepage has exactly one H1 tag. Edit your theme or page builder to add or fix the H1.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_h1_heading' ),
			'score'       => 5,
		);

		$tests['cache_plugin'] = array(
			'title'       => __( 'Caching Plugin', 'seovault-ai' ),
			'description' => __( 'A caching plugin significantly improves page load speed and overall site performance.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Install a caching plugin such as WP Rocket, W3 Total Cache, or WP Super Cache to improve performance.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_cache_plugin' ),
			'score'       => 3,
		);

		$tests['non_www'] = array(
			'title'       => __( 'WWW Consistency', 'seovault-ai' ),
			'description' => __( 'Your site should redirect from the non-preferred version (www or non-www) to the preferred one to avoid duplicate content.', 'seovault-ai' ),
			'how_to_fix'  => '<p>' . __( 'Set your preferred domain in Settings > General and add a redirect from the non-preferred version.', 'seovault-ai' ) . '</p>',
			'category'    => 'advanced',
			'callback'    => array( Tests::class, 'test_non_www' ),
			'score'       => 4,
		);

		$this->tests = apply_filters( 'seovaultseo/analyzer/tests', $tests );
	}

	/**
	 * Get registered tests.
	 *
	 * @return array
	 */
	public function get_tests() {
		return $this->tests;
	}

	/**
	 * Run all tests and store results.
	 *
	 * @return array Results keyed by test ID.
	 */
	public function run_tests() {
		$results = array();

		foreach ( $this->tests as $id => $test ) {
			$test_result = call_user_func( $test['callback'] );

			$results[ $id ] = array_merge(
				array(
					'test_id'     => $id,
					'title'       => $test['title'],
					'description' => $test['description'],
					'how_to_fix'  => isset( $test['how_to_fix'] ) ? $test['how_to_fix'] : '',
					'category'    => $test['category'],
					'score'       => isset( $test['score'] ) ? $test['score'] : 0,
					'info'        => array(),
				),
				$test_result
			);
		}

		$this->results      = $results;
		$this->results_date = time();

		update_option( self::OPTION_RESULTS, $results, false );
		update_option( self::OPTION_DATE, $this->results_date, false );

		return $results;
	}

	/**
	 * Load stored results from the database.
	 *
	 * @return array|false
	 */
	public function load_stored_results() {
		$results = get_option( self::OPTION_RESULTS );
		$date    = get_option( self::OPTION_DATE );

		if ( ! is_array( $results ) ) {
			return false;
		}

		$this->results      = $results;
		$this->results_date = $date;

		return $results;
	}

	/**
	 * Clear stored results.
	 */
	public function clear_results() {
		delete_option( self::OPTION_RESULTS );
		delete_option( self::OPTION_DATE );
		$this->results      = null;
		$this->results_date = null;
	}

	/**
	 * Calculate overall score and metrics.
	 *
	 * @param array $results Results array.
	 * @return array {
	 *   @type int    $total    Total tests run.
	 *   @type int    $percent  Score percentage.
	 *   @type array  $statuses Count by status.
	 *   @type string $grade    'good', 'average', or 'bad'.
	 * }
	 */
	public function get_metrics( $results = null ) {
		if ( null === $results ) {
			$results = $this->results;
		}

		if ( ! is_array( $results ) ) {
			return array(
				'total'    => 0,
				'percent'  => 0,
				'statuses' => array(
					'ok'      => 0,
					'fail'    => 0,
					'warning' => 0,
				),
				'grade'    => 'bad',
			);
		}

		$total       = 0;
		$total_score = 0;
		$earned      = 0;
		$statuses    = array(
			'ok'      => 0,
			'fail'    => 0,
			'warning' => 0,
		);

		foreach ( $results as $id => $result ) {
			if ( ! is_array( $result ) ) {
				continue;
			}

			$status = isset( $result['status'] ) ? $result['status'] : '';
			$score  = isset( $result['score'] ) ? $result['score'] : 0;

			if ( isset( $statuses[ $status ] ) ) {
				++$statuses[ $status ];
			}

			++$total;
			$total_score += $score;

			if ( 'ok' === $status ) {
				$earned += $score;
			} elseif ( 'warning' === $status ) {
				$earned += (int) ( $score / 2 );
			}
		}

		$percent = $total_score > 0 ? (int) round( ( $earned / $total_score ) * 100 ) : 0;

		$grade = 'bad';
		if ( $percent >= 70 ) {
			$grade = 'good';
		} elseif ( $percent >= 50 ) {
			$grade = 'average';
		}

		return array(
			'total'    => $total,
			'percent'  => $percent,
			'statuses' => $statuses,
			'grade'    => $grade,
		);
	}

	/**
	 * Sort results by category (priority first, then basic, then advanced).
	 *
	 * @param array $results Results array.
	 * @return array Grouped by category.
	 */
	public function sort_by_category( $results = null ) {
		if ( null === $results ) {
			$results = $this->results;
		}

		if ( ! is_array( $results ) ) {
			return array();
		}

		$category_order = array(
			'priority' => 0,
			'basic'    => 1,
			'advanced' => 2,
		);
		$grouped        = array();

		foreach ( $results as $id => $result ) {
			$category = isset( $result['category'] ) ? $result['category'] : 'basic';
			if ( ! isset( $grouped[ $category ] ) ) {
				$grouped[ $category ] = array();
			}
			$grouped[ $category ][ $id ] = $result;
		}

		uksort(
			$grouped,
			function ( $a, $b ) use ( $category_order ) {
				$a_order = isset( $category_order[ $a ] ) ? $category_order[ $a ] : 99;
				$b_order = isset( $category_order[ $b ] ) ? $category_order[ $b ] : 99;
				return $a_order - $b_order;
			}
		);

		return $grouped;
	}

	/**
	 * Get formatted last-checked date.
	 *
	 * @return array|false {
	 *   @type string $date
	 *   @type string $time
	 * }
	 */
	public function get_last_checked_date() {
		if ( ! $this->results_date ) {
			return false;
		}

		return array(
			'date' => date_i18n( get_option( 'date_format' ), $this->results_date ),
			'time' => date_i18n( get_option( 'time_format' ), $this->results_date ),
		);
	}
}
