<?php
/**
 * BaseCloud Boost — Lighthouse Sender
 *
 * Receives a Lighthouse/PageSpeed result from the browser (collected in real-time
 * via the Google PageSpeed Insights API) and forwards it as a JSON payload to a
 * user-configured webhook URL.
 *
 * Payload shape:
 * {
 *   "event":            "BCBOOST_lighthouse",
 *   "plugin_version":   "12.0.0",
 *   "site_url":         "https://example.com",
 *   "site_name":        "My Site",
 *   "tested_url":       "https://example.com/",
 *   "strategy":         "mobile",
 *   "timestamp":        "2026-03-22T12:00:00+00:00",
 *   "scores": {
 *     "performance":    95,
 *     "accessibility":  100,
 *     "best_practices": 100,
 *     "seo":            100
 *   },
 *   "metrics": {
 *     "fcp_ms":          800,
 *     "lcp_ms":          1200,
 *     "tbt_ms":          50,
 *     "cls":             0.01,
 *     "speed_index_ms":  900,
 *     "tti_ms":          1500,
 *     "ttfb_ms":         200
 *   }
 * }
 *
 * @package BaseCloud\Boost\Core
 */

declare( strict_types=1 );

namespace BaseCloud\Boost\Core;

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

/**
 * Forwards Lighthouse audit results to a configured webhook URL.
 */
class Lighthouse_Sender {

	/** @var Metrics_Collector */
	private Metrics_Collector $metrics;

	public function __construct() {
		$this->metrics = new Metrics_Collector();
	}

	/**
	 * Send a Lighthouse result payload to the configured (or supplied) webhook URL.
	 *
	 * @param array       $result      Normalised Lighthouse result.
	 * @param string|null $url_override Override stored URL (e.g. for test sends).
	 * @return array{ success: bool, message: string }
	 */
	public function send( array $result, ?string $url_override = null ): array {
		$url = $url_override ?? trim( (string) BCBOOST_get_option( 'lighthouse_webhook_url', '' ) );
		$url = trim( $url );

		if ( empty( $url ) ) {
			return [ 'success' => false, 'message' => __( 'No Lighthouse webhook URL configured.', 'basecloud-boost' ) ];
		}

		if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return [ 'success' => false, 'message' => __( 'Invalid Lighthouse webhook URL.', 'basecloud-boost' ) ];
		}

		$payload = $this->build_payload( $result );
		$body    = (string) wp_json_encode( $payload );

		$headers = [
			'Content-Type' => 'application/json',
			'User-Agent'   => 'BaseCloud-Boost/' . BCBOOST_VERSION,
			'X-BCBoost-Event'  => 'lighthouse',
		];

		$secret = trim( (string) BCBOOST_get_option( 'lighthouse_webhook_secret', '' ) );
		if ( $secret !== '' ) {
			$headers['X-BCBoost-Signature'] = 'sha256=' . hash_hmac( 'sha256', $body, $secret );
		}

		$response = wp_remote_post( $url, [
			'timeout'     => 10,
			'redirection' => 3,
			'headers'     => $headers,
			'body'        => $body,
		] );

		if ( is_wp_error( $response ) ) {
			$this->metrics->increment( 'lighthouse_errors' );
			return [
				'success' => false,
				'message' => $response->get_error_message(),
			];
		}

		$code    = (int) wp_remote_retrieve_response_code( $response );
		$success = $code >= 200 && $code < 300;

		if ( $success ) {
			$this->metrics->set( 'last_lighthouse_sent', time() );
		} else {
			$this->metrics->increment( 'lighthouse_errors' );
		}

		return [
			'success' => $success,
			'message' => $success
				/* translators: HTTP status code */
				? sprintf( __( 'Lighthouse results sent successfully (HTTP %d).', 'basecloud-boost' ), $code )
				/* translators: HTTP status code */
				: sprintf( __( 'Lighthouse webhook failed — server returned HTTP %d.', 'basecloud-boost' ), $code ),
		];
	}

	/**
	 * Run daily Lighthouse audits server-side (mobile + desktop) and send both to the webhook.
	 *
	 * Called by the BCBOOST_lighthouse_daily WP-Cron event.
	 *
	 * @return void
	 */
	public function run_and_send_daily(): void {
		$webhook_url = trim( (string) BCBOOST_get_option( 'lighthouse_webhook_url', '' ) );
		if ( empty( $webhook_url ) ) {
			return;
		}

		$tested_url = trim( (string) BCBOOST_get_option( 'lighthouse_default_url', '' ) );
		if ( empty( $tested_url ) ) {
			$tested_url = get_site_url();
		}

		foreach ( [ 'mobile', 'desktop' ] as $strategy ) {
			$result = $this->fetch_psi( $tested_url, $strategy );
			if ( null !== $result ) {
				$this->send( $result );
			}
		}
	}

	/**
	 * Fetch a PageSpeed Insights audit from the Google API (server-side HTTP call).
	 *
	 * @param string $url      URL to audit.
	 * @param string $strategy 'mobile' or 'desktop'.
	 * @return array|null  Normalised result array, or null on failure.
	 */
	public function fetch_psi( string $url, string $strategy ): ?array {
		$api_key = trim( (string) BCBOOST_get_option( 'lighthouse_psi_api_key', '' ) );

		$endpoint = add_query_arg(
			array_filter( [
				'url'      => $url,
				'strategy' => $strategy,
				'category' => [ 'performance', 'accessibility', 'best-practices', 'seo' ],
				'key'      => $api_key ?: null,
			] ),
			'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'
		);

		// PSI audits can take up to 30 s.
		$response = wp_remote_get( $endpoint, [
			'timeout'     => 60,
			'redirection' => 3,
		] );

		if ( is_wp_error( $response ) ) {
			return null;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( $code < 200 || $code >= 300 ) {
			return null;
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! is_array( $data ) ) {
			return null;
		}

		$cats   = $data['lighthouseResult']['categories'] ?? [];
		$audits = $data['lighthouseResult']['audits']    ?? [];

		$score_of = static function ( string $cat ) use ( $cats ): int {
			return isset( $cats[ $cat ]['score'] ) ? (int) round( $cats[ $cat ]['score'] * 100 ) : 0;
		};

		$ms_of = static function ( string $id ) use ( $audits ): int {
			return isset( $audits[ $id ]['numericValue'] ) ? (int) round( $audits[ $id ]['numericValue'] ) : 0;
		};

		$float_of = static function ( string $id ) use ( $audits ): float {
			return isset( $audits[ $id ]['numericValue'] ) ? (float) $audits[ $id ]['numericValue'] : 0.0;
		};

		return [
			'tested_url' => $url,
			'strategy'   => $strategy,
			'scores'     => [
				'performance'    => $score_of( 'performance' ),
				'accessibility'  => $score_of( 'accessibility' ),
				'best_practices' => $score_of( 'best-practices' ),
				'seo'            => $score_of( 'seo' ),
			],
			'metrics'    => [
				'fcp_ms'         => $ms_of( 'first-contentful-paint' ),
				'lcp_ms'         => $ms_of( 'largest-contentful-paint' ),
				'tbt_ms'         => $ms_of( 'total-blocking-time' ),
				'cls'            => $float_of( 'cumulative-layout-shift' ),
				'speed_index_ms' => $ms_of( 'speed-index' ),
				'tti_ms'         => $ms_of( 'interactive' ),
				'ttfb_ms'        => $ms_of( 'server-response-time' ),
			],
		];
	}

	/**
	 * Wrap the raw Lighthouse result in the standard BCB envelope.
	 *
	 * @param array $result Raw result sent from the browser.
	 * @return array<string, mixed>
	 */
	public function build_payload( array $result ): array {
		return [
			'event'          => 'BCBOOST_lighthouse',
			'plugin_version' => BCBOOST_VERSION,
			'site_url'       => get_site_url(),
			'site_name'      => get_bloginfo( 'name' ),
			'tested_url'     => sanitize_url( (string) ( $result['tested_url'] ?? get_site_url() ) ),
			'strategy'       => in_array( $result['strategy'] ?? '', [ 'mobile', 'desktop' ], true )
								 ? $result['strategy']
								 : 'mobile',
			'timestamp'      => gmdate( 'c' ),
			'scores'         => [
				'performance'    => (int) ( $result['scores']['performance']    ?? 0 ),
				'accessibility'  => (int) ( $result['scores']['accessibility']  ?? 0 ),
				'best_practices' => (int) ( $result['scores']['best_practices'] ?? 0 ),
				'seo'            => (int) ( $result['scores']['seo']            ?? 0 ),
			],
			'metrics'        => [
				'fcp_ms'         => (int) ( $result['metrics']['fcp_ms']         ?? 0 ),
				'lcp_ms'         => (int) ( $result['metrics']['lcp_ms']         ?? 0 ),
				'tbt_ms'         => (int) ( $result['metrics']['tbt_ms']         ?? 0 ),
				'cls'            => (float) ( $result['metrics']['cls']          ?? 0.0 ),
				'speed_index_ms' => (int) ( $result['metrics']['speed_index_ms'] ?? 0 ),
				'tti_ms'         => (int) ( $result['metrics']['tti_ms']         ?? 0 ),
				'ttfb_ms'        => (int) ( $result['metrics']['ttfb_ms']        ?? 0 ),
			],
		];
	}
}
