<?php
/**
 * SEO module.
 *
 * @package MonitorCat\Modules\Seo
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Seo;

defined( 'ABSPATH' ) || exit;

use MonitorCat\Module\AbstractModule;
use MonitorCat\Modules\Seo\Analyzer\AiCrawlerAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\CanonicalAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\GeoReadinessAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\HeadingAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\ImageAltAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\LinkAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\LlmsTxtAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\MetaAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\OpenGraphAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\ReadabilityAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\RobotsAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\SchemaAnalyzer;
use MonitorCat\Modules\Seo\Analyzer\SitemapAnalyzer;
use MonitorCat\Modules\Seo\Generator\LlmsTxtGenerator;
use MonitorCat\Modules\Seo\Generator\MetaTagGenerator;
use MonitorCat\Modules\Seo\Generator\SchemaGenerator;

/**
 * SEO analysis module for evaluating content search engine visibility.
 */
final class SeoModule extends AbstractModule {

	/**
	 * Get the module slug.
	 *
	 * @return string
	 */
	public function slug(): string {
		return 'seo';
	}

	/**
	 * Get the module name.
	 *
	 * @return string
	 */
	public function name(): string {
		return __( 'SEO & GEO', 'monitor-cat' );
	}

	/**
	 * Get the FontAwesome icon class.
	 *
	 * @return string
	 */
	public function icon(): string {
		return 'fa-magnifying-glass-chart';
	}

	/**
	 * Get the module description.
	 *
	 * @return string
	 */
	public function description(): string {
		return __( 'Optimize and improve for Search Engines and AI solutions', 'monitor-cat' );
	}

	/**
	 * Boot the module — register admin hooks and frontend generators.
	 */
	public function boot(): void {
		parent::boot();

		// Frontend generators — run on every page load, not just admin.
		( new SchemaGenerator( $this->settings ) )->boot();
		( new MetaTagGenerator( $this->settings ) )->boot();
		( new LlmsTxtGenerator( $this->settings ) )->boot();

		// Clear site-health transient when SEO settings change.
		add_action( 'update_option_monitorcat_settings', array( $this, 'invalidate_site_health_cache' ), 10, 0 );
	}

	/**
	 * Clear cached site-health data when settings are updated.
	 *
	 * The site-health transient includes the active protections list,
	 * which depends on SEO settings (auto_schema, auto_meta_tags, auto_llms_txt).
	 */
	public function invalidate_site_health_cache(): void {
		delete_transient( 'monitorcat_seo_site_health' );
		delete_transient( 'monitorcat_seo_overview' );
	}

	/**
	 * Register REST API routes for the SEO module.
	 */
	public function register_routes(): void {
		$controller = new RestController();
		$controller->register();
	}

	/**
	 * Get dashboard summary data.
	 *
	 * Runs a quick overview scan of the most recent published post
	 * to provide a snapshot score for the dashboard, using cached
	 * results when available.
	 *
	 * @return array<string, mixed>
	 */
	public function get_dashboard_summary(): array {
		// Check for cached overview first.
		$cached = get_transient( 'monitorcat_seo_overview' );

		if ( false !== $cached && is_array( $cached ) ) {
			return array(
				'score'      => $cached['score'] ?? null,
				'status'     => $cached['status'] ?? 'ready',
				'post_count' => isset( $cached['posts'] ) && is_array( $cached['posts'] ) ? count( $cached['posts'] ) : 0,
				'timestamp'  => $cached['timestamp'] ?? null,
			);
		}

		// Run a quick scan on the latest published post.
		$latest_posts = get_posts(
			array(
				'post_type'              => array( 'post', 'page' ),
				'post_status'            => 'publish',
				'posts_per_page'         => 1,
				'orderby'                => 'date',
				'order'                  => 'DESC',
				'no_found_rows'          => true,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			)
		);

		if ( empty( $latest_posts ) ) {
			return array(
				'score'  => null,
				'status' => 'ready',
			);
		}

		$post_id   = $latest_posts[0]->ID;
		$analyzers = $this->get_analyzers();
		$total     = 0;
		$count     = 0;

		foreach ( $analyzers as $analyzer ) {
			$result = $analyzer->analyze( $post_id );
			$total += $result['score'];
			++$count;
		}

		$score  = $count > 0 ? (int) round( $total / $count ) : 0;
		$status = $this->determine_status( $score );

		return array(
			'score'      => $score,
			'status'     => $status,
			'post_id'    => $post_id,
			'post_title' => get_the_title( $post_id ),
		);
	}

	/**
	 * Get all analyzer instances.
	 *
	 * @return array<string, MetaAnalyzer|OpenGraphAnalyzer|HeadingAnalyzer|ImageAltAnalyzer|SchemaAnalyzer|LinkAnalyzer|SitemapAnalyzer|RobotsAnalyzer|CanonicalAnalyzer|ReadabilityAnalyzer|GeoReadinessAnalyzer|AiCrawlerAnalyzer|LlmsTxtAnalyzer>
	 */
	private function get_analyzers(): array {
		return array(
			'meta'          => new MetaAnalyzer(),
			'open_graph'    => new OpenGraphAnalyzer(),
			'headings'      => new HeadingAnalyzer(),
			'images'        => new ImageAltAnalyzer(),
			'schema'        => new SchemaAnalyzer(),
			'links'         => new LinkAnalyzer(),
			'sitemap'       => new SitemapAnalyzer(),
			'robots'        => new RobotsAnalyzer(),
			'canonical'     => new CanonicalAnalyzer(),
			'readability'   => new ReadabilityAnalyzer(),
			'geo_readiness' => new GeoReadinessAnalyzer(),
			'ai_crawler'    => new AiCrawlerAnalyzer(),
			'llms_txt'      => new LlmsTxtAnalyzer(),
		);
	}

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