<?php
/**
 * Diagnostics module REST controller.
 *
 * @package MonitorCat\Modules\Diagnostics
 */

declare(strict_types=1);

namespace MonitorCat\Modules\Diagnostics;

defined( 'ABSPATH' ) || exit;

use MonitorCat\Modules\Diagnostics\Collector\DatabaseCollector;
use MonitorCat\Modules\Diagnostics\Collector\HealthCollector;
use MonitorCat\Modules\Diagnostics\Collector\PhpCollector;
use MonitorCat\Modules\Diagnostics\Collector\PluginThemeCollector;
use MonitorCat\Modules\Diagnostics\Collector\ServerCollector;
use MonitorCat\Modules\Diagnostics\Collector\WordPressCollector;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Handles REST API endpoints for the Diagnostics module.
 */
final class RestController extends WP_REST_Controller {

	/**
	 * REST API namespace.
	 *
	 * @var string
	 */
	protected $namespace = 'monitor-cat/v1';

	/**
	 * Resource base.
	 *
	 * @var string
	 */
	protected $rest_base = 'diagnostics';

	/**
	 * Register REST API routes.
	 */
	public function register(): void {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/info',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'handle_info' ),
				'permission_callback' => array( $this, 'check_permission' ),
				'args'                => array(
					'force' => array(
						'type'              => 'boolean',
						'default'           => false,
						'sanitize_callback' => 'rest_sanitize_boolean',
					),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/info/(?P<section>[a-z_]+)',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'handle_info_section' ),
				'permission_callback' => array( $this, 'check_permission' ),
				'args'                => array(
					'section' => array(
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'sanitize_key',
					),
				),
			)
		);
	}

	/**
	 * Check if the current user has permission.
	 *
	 * @param WP_REST_Request $request Full data about the request.
	 * @return bool
	 */
	public function check_permission( WP_REST_Request $request ): bool {
		return current_user_can( 'manage_options' );
	}

	/**
	 * Handle the diagnostics info request — returns all sections with health scoring.
	 *
	 * @param WP_REST_Request $request Full data about the request.
	 * @return WP_REST_Response
	 */
	public function handle_info( WP_REST_Request $request ): WP_REST_Response {
		$force = $request->get_param( 'force' );

		if ( ! $force ) {
			$cached = get_transient( 'monitorcat_diag_info' );

			if ( false !== $cached ) {
				return new WP_REST_Response( $cached, 200 );
			}
		}

		$sections = $this->collect_all();
		$response = $this->build_response( $sections );

		set_transient( 'monitorcat_diag_info', $response, 5 * MINUTE_IN_SECONDS );

		return new WP_REST_Response( $response, 200 );
	}

	/**
	 * Handle a single section request.
	 *
	 * @param WP_REST_Request $request Full data about the request.
	 * @return WP_REST_Response|\WP_Error
	 */
	public function handle_info_section( WP_REST_Request $request ) {
		$section    = $request->get_param( 'section' );
		$collectors = $this->get_collectors();

		if ( ! isset( $collectors[ $section ] ) ) {
			return new \WP_Error(
				'invalid_section',
				__( 'Invalid diagnostics section.', 'monitor-cat' ),
				array( 'status' => 404 )
			);
		}

		$collector = $collectors[ $section ];
		$data      = $collector->collect();

		return new WP_REST_Response( $data, 200 );
	}

	/**
	 * Collect all diagnostic sections.
	 *
	 * @return array<string, array<string, mixed>>
	 */
	private function collect_all(): array {
		$sections = array();

		foreach ( $this->get_collectors() as $key => $collector ) {
			$sections[ $key ] = $collector->collect();
		}

		return $sections;
	}

	/**
	 * Build the enriched response with health score and insights.
	 *
	 * @param array<string, array<string, mixed>> $sections Collected section data.
	 * @return array<string, mixed>
	 */
	private function build_response( array $sections ): array {
		$summary  = $this->compute_summary( $sections );
		$insights = $this->extract_insights( $sections );

		return array(
			'health_score'  => $summary['score'],
			'health_status' => $summary['status'],
			'summary'       => $summary,
			'insights'      => $insights,
			'sections'      => $sections,
			'timestamp'     => gmdate( 'c' ),
		);
	}

	/**
	 * Compute health summary from all section items.
	 *
	 * Only items with an explicit status field are counted. Info-only items
	 * (no status) are skipped.
	 *
	 * @param array<string, array<string, mixed>> $sections Collected section data.
	 * @return array{score: int, status: string, total: int, good: int, warnings: int, critical: int}
	 */
	private function compute_summary( array $sections ): array {
		$total    = 0;
		$good     = 0;
		$warnings = 0;
		$critical = 0;

		foreach ( $sections as $section ) {
			if ( ! isset( $section['items'] ) || ! is_array( $section['items'] ) ) {
				continue;
			}

			foreach ( $section['items'] as $item ) {
				if ( ! isset( $item['status'] ) ) {
					continue;
				}

				++$total;

				if ( 'good' === $item['status'] ) {
					++$good;
				} elseif ( 'warning' === $item['status'] ) {
					++$warnings;
				} elseif ( 'critical' === $item['status'] ) {
					++$critical;
				}
			}
		}

		$score = $total > 0 ? (int) round( ( $good / $total ) * 100 ) : 100;

		if ( $critical > 0 ) {
			$status = 'critical';
		} elseif ( $warnings > 0 ) {
			$status = 'warning';
		} else {
			$status = 'good';
		}

		return array(
			'score'    => $score,
			'status'   => $status,
			'total'    => $total,
			'good'     => $good,
			'warnings' => $warnings,
			'critical' => $critical,
		);
	}

	/**
	 * Extract actionable insights from items with warning or critical status.
	 *
	 * @param array<string, array<string, mixed>> $sections Collected section data.
	 * @return array<int, array{label: string, value: string, status: string, section: string, section_label: string}>
	 */
	private function extract_insights( array $sections ): array {
		$insights = array();

		foreach ( $sections as $section_key => $section ) {
			if ( ! isset( $section['items'] ) || ! is_array( $section['items'] ) ) {
				continue;
			}

			$section_label = $section['label'] ?? $section_key;

			foreach ( $section['items'] as $item ) {
				if ( ! isset( $item['status'] ) || 'good' === $item['status'] ) {
					continue;
				}

				$insights[] = array(
					'label'         => $item['label'],
					'value'         => $item['value'],
					'status'        => $item['status'],
					'section'       => $section_key,
					'section_label' => $section_label,
				);
			}
		}

		// Sort critical issues first, then warnings.
		usort(
			$insights,
			function ( array $a, array $b ): int {
				$order = array(
					'critical' => 0,
					'warning'  => 1,
				);

				return ( $order[ $a['status'] ] ?? 2 ) - ( $order[ $b['status'] ] ?? 2 );
			}
		);

		return $insights;
	}

	/**
	 * Get all diagnostic collectors keyed by section name.
	 *
	 * @return array<string, object>
	 */
	private function get_collectors(): array {
		return array(
			'php'           => new PhpCollector(),
			'wordpress'     => new WordPressCollector(),
			'database'      => new DatabaseCollector(),
			'server'        => new ServerCollector(),
			'plugins_theme' => new PluginThemeCollector(),
			'health'        => new HealthCollector(),
		);
	}
}
