<?php

namespace AtlasAiDev\Awfah\Service;

use AtlasAiDev\Awfah\Abilities\Debug_Log_Abilities;
use AtlasAiDev\Awfah\Plugin;

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

/**
 * Real-time PHP error email notification service.
 *
 * Hooks into PHP's error handler and shutdown function to detect errors in
 * plugins and themes, then sends batched HTML email notifications with
 * human-readable advice. Pro users can opt-in to AI-powered solutions.
 */
class Error_Email_Notifier {

	/** @var self|null */
	private static ?self $instance = null;

	/** @var array Buffered errors for batching within one request. */
	private array $error_buffer = [];

	/** @var bool Whether we registered a flush on shutdown. */
	private bool $flush_registered = false;

	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {}

	// ──────────────────────────────────────────────
	// Settings helpers
	// ──────────────────────────────────────────────

	/**
	 * Default notification settings.
	 *
	 * @return array
	 */
	public static function get_default_notification_settings(): array {
		return [
			'enabled'              => false,
			'email'                => '',
			'severities'           => [ 'fatal', 'warning' ],
			'cooldown_minutes'     => 60,
			'sources'              => [ 'plugin', 'theme' ],
			'webhook_enabled'      => false,
			'webhook_url'          => '',
			'webhook_channel'      => 'slack',
			'webhook_events'       => [ 'fatal' ],
			'weekly_digest'        => false,
			'spike_alerts'         => true,
			'log_file_path'        => '',
			'show_dashboard_widget' => true,
		];
	}

	/**
	 * Merged notification settings (defaults + saved).
	 *
	 * @return array
	 */
	public static function get_notification_settings(): array {
		$settings     = Plugin::get_settings();
		$notification = $settings['error_notification'] ?? [];
		return wp_parse_args( $notification, self::get_default_notification_settings() );
	}

	// ──────────────────────────────────────────────
	// Error handler registration
	// ──────────────────────────────────────────────

	/**
	 * Register PHP error handler and shutdown function.
	 */
	public function register_handlers(): void {
		$settings = self::get_notification_settings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}

		set_error_handler( [ $this, 'handle_error' ] );
		register_shutdown_function( [ $this, 'handle_shutdown' ] );
	}

	/**
	 * Custom error handler. Returns false so PHP's default handler still runs.
	 *
	 * @param int    $errno   Error level.
	 * @param string $errstr  Error message.
	 * @param string $errfile File where the error occurred.
	 * @param int    $errline Line number.
	 * @return bool Always false.
	 */
	public function handle_error( int $errno, string $errstr, string $errfile, int $errline ): bool {
		// Respect error_reporting level (e.g. @-suppressed errors).
		if ( ! ( error_reporting() & $errno ) ) {
			return false;
		}

		$severity = $this->map_errno_to_severity( $errno );
		if ( '' === $severity ) {
			return false;
		}

		$this->process_error( $severity, $errstr, $errfile, $errline );
		return false;
	}

	/**
	 * Shutdown handler to capture fatal errors.
	 */
	public function handle_shutdown(): void {
		$error = error_get_last();
		if ( null !== $error ) {
			$fatal_types = [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR ];
			if ( in_array( $error['type'], $fatal_types, true ) ) {
				$this->process_error( 'fatal', $error['message'], $error['file'], $error['line'] );
			}
		}

		$this->flush_buffer();
	}

	// ──────────────────────────────────────────────
	// Debug log monitoring (catches early fatal errors)
	// ──────────────────────────────────────────────

	/**
	 * Settings key for storing the last-checked debug.log byte position
	 * inside the awfah_settings['error_notification'] array.
	 */
	const LOG_POSITION_KEY = 'log_position';

	/**
	 * Get the last-checked log position from settings.
	 *
	 * @return int Byte offset.
	 */
	private static function get_log_position(): int {
		$settings = Plugin::get_settings();
		return (int) ( $settings['error_notification'][ self::LOG_POSITION_KEY ] ?? 0 );
	}

	/**
	 * Save the last-checked log position into settings.
	 *
	 * @param int $position Byte offset.
	 */
	private static function set_log_position( int $position ): void {
		$settings = Plugin::get_settings();
		$settings['error_notification']                        = $settings['error_notification'] ?? [];
		$settings['error_notification'][ self::LOG_POSITION_KEY ] = $position;
		Plugin::update_settings( $settings );
	}

	/**
	 * Check debug.log for errors that our shutdown handler missed.
	 *
	 * This catches fatal/parse errors that occur before our plugin loads
	 * (e.g. during plugin activation when the problematic plugin is loaded
	 * alphabetically before ours in wp-settings.php).
	 *
	 * Hooked to admin_init.
	 */
	public static function check_for_missed_errors(): void {
		$settings = self::get_notification_settings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}

		$log_path = Debug_Log_Abilities::get_log_path();
		if ( ! file_exists( $log_path ) || ! is_readable( $log_path ) ) {
			return;
		}

		$file_size     = (int) filesize( $log_path );
		$last_position = self::get_log_position();

		// If file is smaller than last position, it was cleared/rotated.
		if ( $file_size < $last_position ) {
			$last_position = 0;
		}

		// Nothing new to read.
		if ( $file_size <= $last_position ) {
			return;
		}

		// Read only new content (max 64KB to avoid memory issues).
		$max_read   = 65536;
		$read_start = max( $last_position, $file_size - $max_read );
		$read_bytes = $file_size - $read_start;

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
		$handle = fopen( $log_path, 'rb' );
		if ( ! $handle ) {
			return;
		}

		fseek( $handle, $read_start );
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
		$new_content = fread( $handle, $read_bytes );
		fclose( $handle );

		// Update position immediately to avoid re-processing on next request.
		self::set_log_position( $file_size );

		if ( empty( $new_content ) ) {
			return;
		}

		$lines    = explode( "\n", $new_content );
		$instance = self::instance();
		$enabled  = $settings['severities'] ?? [];

		foreach ( $lines as $line ) {
			$line = trim( $line );
			if ( '' === $line ) {
				continue;
			}

			$parsed = Debug_Log_Abilities::parse_log_line( $line );
			if ( null === $parsed || '' === $parsed['file'] ) {
				continue;
			}

			// Filter by configured error sources.
			$allowed_sources = $settings['sources'] ?? [ 'plugin', 'theme' ];
			if ( ! in_array( $parsed['source_type'], $allowed_sources, true ) ) {
				continue;
			}

			// Check severity against enabled list.
			if ( ! in_array( $parsed['severity'], $enabled, true ) ) {
				continue;
			}

			// Rate-limit check.
			$hash = $instance->get_error_hash( $parsed['file'], $parsed['line'], $parsed['message'] );
			if ( $instance->is_rate_limited( $hash ) ) {
				continue;
			}

			$missed_error = [
				'severity'    => $parsed['severity'],
				'message'     => $parsed['message'],
				'file'        => $parsed['file'],
				'line'        => $parsed['line'],
				'source_type' => $parsed['source_type'],
				'source_name' => $parsed['source_name'],
				'timestamp'   => $parsed['timestamp'] ?? current_time( 'mysql' ),
				'hash'        => $hash,
			];

			// Track in error groups.
			Error_Tracker::instance()->track_error( $missed_error );

			$instance->buffer_error( $missed_error );
		}

		$instance->flush_buffer();
	}

	/**
	 * Get the current log monitor status for dashboard display.
	 *
	 * @return array{debug_enabled: bool, log_enabled: bool, log_path: string, log_exists: bool, log_size: int, log_size_human: string, last_checked_position: int, pending_bytes: int, coverage: string}
	 */
	public static function get_log_monitor_status(): array {
		$debug_enabled = defined( 'WP_DEBUG' ) && WP_DEBUG;
		$log_enabled   = defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG;
		$log_path      = Debug_Log_Abilities::get_log_path();
		$log_exists    = file_exists( $log_path );
		$file_size     = $log_exists ? (int) filesize( $log_path ) : 0;
		$last_position = self::get_log_position();
		$pending       = max( 0, $file_size - $last_position );

		// Auto-detected path (ignoring user override) for UI placeholder.
		if ( defined( 'WP_DEBUG_LOG' ) && is_string( WP_DEBUG_LOG ) && '' !== WP_DEBUG_LOG ) {
			$default_log_path = WP_DEBUG_LOG;
		} else {
			$default_log_path = WP_CONTENT_DIR . '/debug.log';
		}

		// Determine coverage level.
		if ( $log_enabled && $log_exists ) {
			$coverage = 'full';
		} elseif ( $debug_enabled ) {
			$coverage = 'partial';
		} else {
			$coverage = 'runtime_only';
		}

		return [
			'debug_enabled'         => $debug_enabled,
			'log_enabled'           => $log_enabled,
			'log_path'              => $log_path,
			'default_log_path'      => $default_log_path,
			'log_exists'            => $log_exists,
			'log_size'              => $file_size,
			'log_size_human'        => $log_exists ? size_format( $file_size ) : '0 B',
			'last_checked_position' => $last_position,
			'pending_bytes'         => $pending,
			'coverage'              => $coverage,
		];
	}

	/**
	 * Reset the log scan position so the next admin page load re-scans the entire log.
	 *
	 * @return array{success: bool, message: string}
	 */
	public static function reset_log_position(): array {
		self::set_log_position( 0 );

		return [
			'success' => true,
			'message' => __( 'Log scan position reset. New errors will be detected on next admin page load.', 'ai-workflow-automation-ai-agent-hub' ),
		];
	}

	/**
	 * Trigger an immediate rescan of the debug log and return results.
	 *
	 * @return array{success: bool, errors_found: int, message: string}
	 */
	public static function rescan_log(): array {
		self::set_log_position( 0 );
		self::check_for_missed_errors();

		$status = self::get_log_monitor_status();

		return [
			'success' => true,
			'message' => __( 'Log rescan complete.', 'ai-workflow-automation-ai-agent-hub' ),
			'status'  => $status,
		];
	}

	/**
	 * Common processing: filter, rate-limit, buffer.
	 *
	 * @param string $severity Severity string.
	 * @param string $message  Error message.
	 * @param string $file     File path.
	 * @param int    $line     Line number.
	 */
	private function process_error( string $severity, string $message, string $file, int $line ): void {
		$settings = self::get_notification_settings();

		// Check severity is in the enabled list.
		if ( ! in_array( $severity, $settings['severities'] ?? [], true ) ) {
			return;
		}

		// Only plugin / theme errors.
		if ( ! $this->is_notifiable_error( $file ) ) {
			return;
		}

		// Rate-limit check.
		$hash = $this->get_error_hash( $file, $line, $message );
		if ( $this->is_rate_limited( $hash ) ) {
			return;
		}

		$source = Debug_Log_Abilities::identify_source( $file );

		$error_data = [
			'severity'    => $severity,
			'message'     => $message,
			'file'        => $file,
			'line'        => $line,
			'source_type' => $source['source_type'],
			'source_name' => $source['source_name'],
			'timestamp'   => current_time( 'mysql' ),
			'hash'        => $hash,
		];

		// Track in error groups for Error Log / Trends.
		Error_Tracker::instance()->track_error( $error_data );

		$this->buffer_error( $error_data );
	}

	// ──────────────────────────────────────────────
	// Error filtering
	// ──────────────────────────────────────────────

	/**
	 * Check whether a file path belongs to a plugin or theme.
	 *
	 * @param string $file Absolute file path.
	 * @return bool
	 */
	private function is_notifiable_error( string $file ): bool {
		$source   = Debug_Log_Abilities::identify_source( $file );
		$settings = self::get_notification_settings();
		$allowed  = $settings['sources'] ?? [ 'plugin', 'theme' ];
		return in_array( $source['source_type'], $allowed, true );
	}

	/**
	 * Map PHP error constant to a severity string.
	 *
	 * @param int $errno PHP error constant.
	 * @return string Severity or empty string if not mapped.
	 */
	private function map_errno_to_severity( int $errno ): string {
		$map = [
			E_ERROR             => 'fatal',
			E_PARSE             => 'fatal',
			E_CORE_ERROR        => 'fatal',
			E_COMPILE_ERROR     => 'fatal',
			E_USER_ERROR        => 'fatal',
			E_WARNING           => 'warning',
			E_CORE_WARNING      => 'warning',
			E_COMPILE_WARNING   => 'warning',
			E_USER_WARNING      => 'warning',
			E_NOTICE            => 'notice',
			E_USER_NOTICE       => 'notice',
			E_DEPRECATED        => 'deprecated',
			E_USER_DEPRECATED   => 'deprecated',
			E_STRICT            => 'notice',
		];

		return $map[ $errno ] ?? '';
	}

	// ──────────────────────────────────────────────
	// Rate limiting (WordPress transients)
	// ──────────────────────────────────────────────

	/**
	 * Generate a unique hash for an error.
	 *
	 * @param string $file    File path.
	 * @param int    $line    Line number.
	 * @param string $message Error message.
	 * @return string Transient key.
	 */
	private function get_error_hash( string $file, int $line, string $message ): string {
		return 'awfah_err_' . md5( $file . ':' . $line . ':' . $message );
	}

	/**
	 * Check if an error was already notified within the cooldown period.
	 *
	 * @param string $hash Transient key.
	 * @return bool
	 */
	private function is_rate_limited( string $hash ): bool {
		return false !== get_transient( $hash );
	}

	/**
	 * Mark an error as sent (set transient for cooldown period).
	 *
	 * @param string $hash             Transient key.
	 * @param int    $cooldown_minutes Cooldown in minutes.
	 */
	private function mark_as_sent( string $hash, int $cooldown_minutes ): void {
		set_transient( $hash, time(), $cooldown_minutes * MINUTE_IN_SECONDS );
	}

	// ──────────────────────────────────────────────
	// Error buffering & batching
	// ──────────────────────────────────────────────

	/**
	 * Add an error to the request buffer.
	 *
	 * @param array $error_data Parsed error data.
	 */
	private function buffer_error( array $error_data ): void {
		$this->error_buffer[] = $error_data;
	}

	/**
	 * Send all buffered errors as a single email and reset buffer.
	 */
	public function flush_buffer(): void {
		if ( empty( $this->error_buffer ) ) {
			return;
		}

		$errors             = $this->error_buffer;
		$this->error_buffer = [];

		// Mark all errors as rate-limited.
		$settings = self::get_notification_settings();
		$cooldown = max( 1, (int) ( $settings['cooldown_minutes'] ?? 60 ) );

		foreach ( $errors as $error ) {
			$this->mark_as_sent( $error['hash'], $cooldown );
		}

		$this->send_notification( $errors );
	}

	// ──────────────────────────────────────────────
	// Email sending
	// ──────────────────────────────────────────────

	/**
	 * Send a notification email for the given errors.
	 *
	 * @param array $errors Array of error data arrays.
	 */
	private function send_notification( array $errors ): void {
		$settings  = self::get_notification_settings();
		$recipient = ! empty( $settings['email'] ) ? $settings['email'] : get_option( 'admin_email' );

		if ( empty( $recipient ) ) {
			return;
		}

		$count   = count( $errors );
		$site    = get_bloginfo( 'name' );
		$subject = sprintf(
			/* translators: 1: error count, 2: site name */
			_n(
				'[%2$s] %1$d PHP Error Detected',
				'[%2$s] %1$d PHP Errors Detected',
				$count,
				'ai-workflow-automation-ai-agent-hub'
			),
			$count,
			$site
		);

		$html    = $this->build_email_html( $errors );
		$headers = [ 'Content-Type: text/html; charset=UTF-8' ];

		wp_mail( $recipient, $subject, $html, $headers );

		// Dispatch webhook notification (Pro handles via filter).
		apply_filters( 'awfah_error_webhook_dispatch', false, $errors, $settings );
	}

	// ──────────────────────────────────────────────
	// Email HTML template
	// ──────────────────────────────────────────────

	/**
	 * Build the notification email body.
	 *
	 * @param array $errors Array of error data arrays.
	 * @return string HTML email body.
	 */
	private function build_email_html( array $errors ): string {
		$site_name = esc_html( get_bloginfo( 'name' ) );
		$site_url  = esc_html( home_url() );
		$count     = count( $errors );

		$html  = '<!DOCTYPE html><html><head><meta charset="utf-8"></head>';
		$html .= '<body style="font-family:-apple-system,system-ui,BlinkMacSystemFont,\'Segoe UI\',sans-serif;max-width:600px;margin:0 auto;padding:20px;background:#f8fafc;">';

		// Header.
		$html .= '<div style="background:#1e293b;color:#fff;padding:16px 20px;border-radius:12px 12px 0 0;">';
		$html .= '<h2 style="margin:0;font-size:16px;">PHP Error Notification</h2>';
		$html .= '<p style="margin:4px 0 0;font-size:12px;opacity:0.8;">' . $site_name . ' &mdash; ' . $site_url . '</p>';
		$html .= '</div>';

		// Summary.
		$html .= '<div style="background:#fff;border:1px solid #e2e8f0;border-top:none;padding:12px 20px;">';
		$html .= '<strong>' . sprintf(
			/* translators: %d: number of errors */
			_n( '%d error detected', '%d errors detected', $count, 'ai-workflow-automation-ai-agent-hub' ),
			$count
		) . '</strong>';
		$html .= '</div>';

		// Individual errors.
		foreach ( $errors as $error ) {
			$badge = $this->get_severity_badge( $error['severity'] );

			$html .= '<div style="background:#fff;border:1px solid #e2e8f0;border-top:none;padding:16px 20px;">';
			$html .= $badge;
			$html .= '<p style="margin:8px 0 4px;font-weight:600;font-size:14px;">' . esc_html( $error['message'] ) . '</p>';
			$html .= '<p style="margin:0;font-size:13px;color:#64748b;">';
			$html .= '<strong>' . esc_html__( 'File:', 'ai-workflow-automation-ai-agent-hub' ) . '</strong> ' . esc_html( $error['file'] ) . ' (' . esc_html__( 'line', 'ai-workflow-automation-ai-agent-hub' ) . ' ' . (int) $error['line'] . ')<br>';
			$html .= '<strong>' . esc_html__( 'Source:', 'ai-workflow-automation-ai-agent-hub' ) . '</strong> ' . esc_html( ucfirst( $error['source_type'] ) . ': ' . $error['source_name'] ) . '<br>';
			$html .= '<strong>' . esc_html__( 'Time:', 'ai-workflow-automation-ai-agent-hub' ) . '</strong> ' . esc_html( $error['timestamp'] );
			$html .= '</p>';

			// How to Fix block.
			$default_command = sprintf(
				"Error: %s\nFile: %s:%d\nSource: %s - %s",
				$error['message'],
				$error['file'],
				$error['line'],
				ucfirst( $error['source_type'] ),
				$error['source_name']
			);

			/**
			 * Generate a fix command for the error email.
			 *
			 * @param array  $fix_data   Fix command data with 'command' and 'instruction' keys.
			 * @param array  $error_data Error details.
			 * @return array Fix command data.
			 */
			$fix_data = apply_filters( 'awfah_error_email_fix_command', [
				'command'     => $default_command,
				'instruction' => __( 'Copy this and paste into any AI assistant to get help fixing this error.', 'ai-workflow-automation-ai-agent-hub' ),
			], $error );

			if ( ! empty( $fix_data['command'] ) ) {
				$html .= '<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:16px;margin-top:12px;">';
				$html .= '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">';
				$html .= '<strong style="font-size:13px;">' . esc_html__( 'How to Fix', 'ai-workflow-automation-ai-agent-hub' ) . '</strong>';
				$html .= '<span style="display:inline-block;background:#4f46e5;color:#fff;padding:4px 12px;border-radius:4px;font-size:11px;font-weight:600;letter-spacing:0.5px;">' . esc_html__( 'SELECT & COPY', 'ai-workflow-automation-ai-agent-hub' ) . ' &#8595;</span>';
				$html .= '</div>';
				$html .= '<p style="margin:0 0 8px;font-size:12px;color:#64748b;">' . wp_kses_post( $fix_data['instruction'] ) . '</p>';

				// Pro: MCP server name note (added by filter).
				if ( ! empty( $fix_data['mcp_note'] ) ) {
					$html .= '<div style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#1e40af;">';
					$html .= wp_kses_post( $fix_data['mcp_note'] );
					$html .= '</div>';
				}

				$html .= '<pre style="background:#1e293b;color:#e2e8f0;padding:14px;border-radius:6px;font-size:12px;white-space:pre-wrap;word-wrap:break-word;font-family:\'Courier New\',monospace;margin:0;border:2px solid #4f46e5;cursor:text;user-select:all;-webkit-user-select:all;-moz-user-select:all;">' . esc_html( $fix_data['command'] ) . '</pre>';
				$html .= '<p style="margin:6px 0 0;font-size:11px;color:#94a3b8;text-align:center;">' . esc_html__( 'Click the box above to select all text, then Ctrl+C (Cmd+C on Mac) to copy', 'ai-workflow-automation-ai-agent-hub' ) . '</p>';
				$html .= '</div>';
			}

			// Pro upsell (only when Pro is NOT active).
			if ( ! awfah_is_pro_active() ) {
				$pro_url = 'https://atlasaidev.com/ai-agent-hub-pro/';
				$html .= '<div style="background:linear-gradient(135deg,#312e81,#6c63ff);border-radius:10px;padding:20px;margin-top:16px;color:#fff;text-align:center;">';
				$html .= '<p style="margin:0 0 4px;font-size:11px;font-weight:600;letter-spacing:1px;color:#c7d2fe;">' . esc_html__( 'UPGRADE TO PRO', 'ai-workflow-automation-ai-agent-hub' ) . '</p>';
				$html .= '<p style="margin:0 0 12px;font-size:15px;font-weight:700;">' . esc_html__( 'Fix errors automatically with AI', 'ai-workflow-automation-ai-agent-hub' ) . '</p>';
				$html .= '<p style="margin:0 0 16px;font-size:12px;color:#c7d2fe;line-height:1.5;">';
				$html .= esc_html__( 'Pro users get a one-click fix command that connects to your WordPress MCP server. AI analyzes the error, suggests a fix, creates a backup, and applies it — all from your AI assistant.', 'ai-workflow-automation-ai-agent-hub' );
				$html .= '</p>';
				$html .= '<div style="margin-bottom:12px;">';
				$html .= '<span style="display:inline-block;font-size:11px;color:#a5b4fc;margin:0 8px;">&#10003; ' . esc_html__( 'AI-powered error analysis', 'ai-workflow-automation-ai-agent-hub' ) . '</span>';
				$html .= '<span style="display:inline-block;font-size:11px;color:#a5b4fc;margin:0 8px;">&#10003; ' . esc_html__( 'Automatic backup before fix', 'ai-workflow-automation-ai-agent-hub' ) . '</span>';
				$html .= '<span style="display:inline-block;font-size:11px;color:#a5b4fc;margin:0 8px;">&#10003; ' . esc_html__( 'MCP tool integration', 'ai-workflow-automation-ai-agent-hub' ) . '</span>';
				$html .= '</div>';
				$html .= '<a href="' . esc_url( $pro_url ) . '" style="display:inline-block;background:#10b981;color:#fff;padding:10px 24px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:600;">' . esc_html__( 'Get AI Agent Hub Pro', 'ai-workflow-automation-ai-agent-hub' ) . ' &rarr;</a>';
				$html .= '</div>';
			}

			$html .= '</div>';
		}

		// Footer.
		$admin_url       = admin_url( 'admin.php?page=ai-workflow-automation-ai-agent-hub' );
		$unsubscribe_url = self::get_unsubscribe_url();

		$html .= '<div style="background:#f8fafc;border:1px solid #e2e8f0;border-top:none;border-radius:0 0 12px 12px;padding:16px 20px;text-align:center;">';
		$html .= '<a href="' . esc_url( $admin_url ) . '" style="display:inline-block;background:#4f46e5;color:#fff;padding:8px 16px;border-radius:6px;text-decoration:none;font-size:13px;font-weight:600;">' . esc_html__( 'View Error Debugger', 'ai-workflow-automation-ai-agent-hub' ) . '</a>';
		$html .= '<p style="margin:12px 0 0;font-size:11px;color:#94a3b8;">';
		$html .= esc_html__( 'Sent by AI Agent Hub', 'ai-workflow-automation-ai-agent-hub' ) . ' &mdash; ';
		$html .= '<a href="' . esc_url( $unsubscribe_url ) . '" style="color:#94a3b8;">' . esc_html__( 'Disable notifications', 'ai-workflow-automation-ai-agent-hub' ) . '</a>';
		$html .= '</p></div>';

		$html .= '</body></html>';
		return $html;
	}

	/**
	 * Return an inline-styled severity badge.
	 *
	 * @param string $severity Severity level.
	 * @return string HTML badge.
	 */
	private function get_severity_badge( string $severity ): string {
		$colors = [
			'fatal'      => 'background:#fef2f2;color:#991b1b;border:1px solid #fecaca;',
			'warning'    => 'background:#fffbeb;color:#92400e;border:1px solid #fde68a;',
			'notice'     => 'background:#fefce8;color:#854d0e;border:1px solid #fef08a;',
			'deprecated' => 'background:#f1f5f9;color:#475569;border:1px solid #e2e8f0;',
		];

		$style = $colors[ $severity ] ?? $colors['notice'];
		$label = strtoupper( $severity );

		return '<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:700;letter-spacing:0.5px;' . $style . '">' . esc_html( $label ) . '</span>';
	}

	// ──────────────────────────────────────────────
	// Solutions
	// ──────────────────────────────────────────────

	/**
	 * Attempt to get an AI-powered solution (Pro only, opt-in).
	 *
	 * @param array $error_data Error details.
	 * @return string AI solution or empty string.
	 */
	private function get_ai_solution( array $error_data ): string {
		$settings = self::get_notification_settings();

		if ( empty( $settings['include_ai_solution'] ) ) {
			return '';
		}

		/**
		 * Filter: generate an AI-powered human-readable solution for an error.
		 *
		 * @param string $solution   Default empty string.
		 * @param array  $error_data Error details (severity, message, file, line, source_type, source_name).
		 * @return string Human-readable solution text.
		 */
		$solution = apply_filters( 'awfah_error_email_ai_solution', '', $error_data );

		return is_string( $solution ) ? $solution : '';
	}

	/**
	 * Generic human-readable tips based on error severity and message patterns.
	 *
	 * @param string $severity Error severity.
	 * @param string $message  Error message.
	 * @return string Advice text.
	 */
	private function get_generic_tips( string $severity, string $message ): string {
		$msg_lower = strtolower( $message );

		if ( 'fatal' === $severity ) {
			if ( false !== strpos( $msg_lower, 'allowed memory size' ) ) {
				return __( 'This is a memory exhaustion error. Consider increasing the PHP memory limit in wp-config.php by adding: define( \'WP_MEMORY_LIMIT\', \'256M\' );', 'ai-workflow-automation-ai-agent-hub' );
			}
			if ( false !== strpos( $msg_lower, 'call to undefined function' ) ) {
				return __( 'A required function is missing. This usually means a dependent plugin or library is deactivated or was not installed properly. Check if all required plugins are active.', 'ai-workflow-automation-ai-agent-hub' );
			}
			if ( false !== strpos( $msg_lower, 'class' ) && false !== strpos( $msg_lower, 'not found' ) ) {
				return __( 'A PHP class could not be loaded. Verify the autoloader is working and that the required plugin or dependency is installed and up to date.', 'ai-workflow-automation-ai-agent-hub' );
			}
			if ( false !== strpos( $msg_lower, 'maximum execution time' ) ) {
				return __( 'The script exceeded the maximum execution time. This might be caused by an infinite loop or a very slow database query. Consider increasing max_execution_time in php.ini or optimizing the plugin code.', 'ai-workflow-automation-ai-agent-hub' );
			}
			return __( 'A fatal error stopped PHP execution. Check the file and line number shown above. You may need to deactivate the plugin or theme causing the error and contact its developer.', 'ai-workflow-automation-ai-agent-hub' );
		}

		if ( 'warning' === $severity ) {
			if ( false !== strpos( $msg_lower, 'undefined variable' ) || false !== strpos( $msg_lower, 'undefined array key' ) || false !== strpos( $msg_lower, 'undefined index' ) ) {
				return __( 'A variable or array key is being used before it is defined. This is usually a minor coding issue in the plugin or theme. Contact the developer to add proper initialization.', 'ai-workflow-automation-ai-agent-hub' );
			}
			if ( false !== strpos( $msg_lower, 'include' ) || false !== strpos( $msg_lower, 'require' ) ) {
				return __( 'A required file could not be found. Verify the plugin or theme is fully installed and no files are missing. Try reinstalling it.', 'ai-workflow-automation-ai-agent-hub' );
			}
			return __( 'PHP warnings indicate potential issues that may cause unexpected behavior. Review the source plugin or theme and consider updating to the latest version.', 'ai-workflow-automation-ai-agent-hub' );
		}

		if ( 'notice' === $severity ) {
			return __( 'PHP notices are non-critical but may indicate coding issues. Consider contacting the plugin or theme developer to improve code quality.', 'ai-workflow-automation-ai-agent-hub' );
		}

		if ( 'deprecated' === $severity ) {
			return __( 'This code uses a deprecated function or feature that will be removed in a future version. Update the plugin or theme to its latest version, or contact its developer for a compatibility update.', 'ai-workflow-automation-ai-agent-hub' );
		}

		return '';
	}

	// ──────────────────────────────────────────────
	// Test email
	// ──────────────────────────────────────────────

	/**
	 * Send a test notification email.
	 *
	 * @return array{success: bool, recipient: string, message: string}
	 */
	public static function send_test_email(): array {
		$settings  = self::get_notification_settings();
		$recipient = ! empty( $settings['email'] ) ? $settings['email'] : get_option( 'admin_email' );

		$test_error = [
			'severity'    => 'warning',
			'message'     => __( 'This is a test error notification from AI Agent Hub. If you received this email, your error notifications are working correctly.', 'ai-workflow-automation-ai-agent-hub' ),
			'file'        => WP_CONTENT_DIR . '/plugins/example-plugin/example.php',
			'line'        => 42,
			'source_type' => 'plugin',
			'source_name' => 'example-plugin',
			'timestamp'   => current_time( 'mysql' ),
			'hash'        => 'test',
		];

		$instance = self::instance();
		$html     = $instance->build_email_html( [ $test_error ] );

		$subject = sprintf(
			/* translators: %s: site name */
			__( '[Test] PHP Error Notification — %s', 'ai-workflow-automation-ai-agent-hub' ),
			get_bloginfo( 'name' )
		);

		$headers = [ 'Content-Type: text/html; charset=UTF-8' ];
		$sent    = wp_mail( $recipient, $subject, $html, $headers );

		return [
			'success'   => $sent,
			'recipient' => $recipient,
			'message'   => $sent
				? sprintf(
					/* translators: %s: email address */
					__( 'Test email sent to %s.', 'ai-workflow-automation-ai-agent-hub' ),
					$recipient
				)
				: __( 'Failed to send test email. Check your WordPress email configuration.', 'ai-workflow-automation-ai-agent-hub' ),
		];
	}

	// ──────────────────────────────────────────────
	// Unsubscribe
	// ──────────────────────────────────────────────

	/**
	 * Generate the unsubscribe URL with HMAC token.
	 *
	 * @return string
	 */
	public static function get_unsubscribe_url(): string {
		return add_query_arg(
			[
				'awfah_action' => 'unsubscribe_error_notifications',
				'token'        => self::generate_unsubscribe_token(),
			],
			home_url()
		);
	}

	/**
	 * Generate an HMAC token for the unsubscribe link.
	 *
	 * @return string
	 */
	public static function generate_unsubscribe_token(): string {
		return hash_hmac( 'sha256', 'awfah_unsubscribe_error_notifications', wp_salt( 'nonce' ) );
	}

	/**
	 * Verify an unsubscribe token.
	 *
	 * @param string $token Token to verify.
	 * @return bool
	 */
	public static function verify_unsubscribe_token( string $token ): bool {
		return hash_equals( self::generate_unsubscribe_token(), $token );
	}

	/**
	 * Handle unsubscribe request (hooked to template_redirect and admin_init).
	 */
	public static function handle_unsubscribe(): void {
		if ( ! isset( $_GET['awfah_action'] ) || 'unsubscribe_error_notifications' !== $_GET['awfah_action'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		$token = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! self::verify_unsubscribe_token( $token ) ) {
			wp_die(
				esc_html__( 'Invalid or expired unsubscribe link.', 'ai-workflow-automation-ai-agent-hub' ),
				esc_html__( 'Error', 'ai-workflow-automation-ai-agent-hub' ),
				[ 'response' => 403 ]
			);
		}

		$settings                                      = Plugin::get_settings();
		$settings['error_notification']                = array_merge(
			self::get_default_notification_settings(),
			$settings['error_notification'] ?? [],
			[ 'enabled' => false ]
		);
		Plugin::update_settings( $settings );

		wp_die(
			esc_html__( 'Error email notifications have been disabled. You can re-enable them from the AI Agent Hub dashboard.', 'ai-workflow-automation-ai-agent-hub' ),
			esc_html__( 'Notifications Disabled', 'ai-workflow-automation-ai-agent-hub' ),
			[ 'response' => 200 ]
		);
	}
}
