<?php
/**
 * REST API Test Controller.
 *
 * Handles sending test emails with PHPMailer debug capture.
 *
 * @package NimblyMail\SMTP
 *
 * @since 1.0.0
 */

namespace NimblyMail\SMTP\Rest;

use NimblyMail\SMTP\Options;
use NimblyMail\SMTP\Processor;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

defined( 'ABSPATH' ) || exit;

/**
 * Class TestController
 *
 * Sends a test email and captures SMTP debug output.
 *
 * @since 1.0.0
 * @since 1.0.0 Added recipient allowlist enforcement with nimblymail_test_email_recipients filter and NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED constant override.
 */
class TestController extends AbstractController {

    /**
     * Register REST routes.
     *
     * @since 1.0.0
     */
    public function register_routes(): void {
        register_rest_route(
            self::NAMESPACE,
            '/test',
            array(
                array(
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => array( $this, 'send_test' ),
                    'permission_callback' => array( $this, 'permissions_check' ),
                    'args'                => array(
                        'to' => array(
                            'type'              => 'string',
                            'sanitize_callback' => 'sanitize_email',
                        ),
                    ),
                ),
            )
        );
    }

    /**
     * Send a test email and capture debug output.
     *
     * Enforces a rate limit of 5 requests per minute per user. By default,
     * the recipient is restricted to the site's admin email address.
     *
     * To allow additional recipients, use the nimblymail_test_email_recipients
     * filter to return an array of permitted addresses, or define the constant
     * NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED as true to disable the check entirely.
     *
     * @since 1.0.0
     * @since 1.0.0 Added recipient allowlist enforcement.
     *
     * @param WP_REST_Request $request The request object.
     *
     * @return WP_REST_Response|WP_Error
     */
    public function send_test( WP_REST_Request $request ) {
        $rate_error = $this->check_rate_limit( 'test_rate', 5, 60 );
        if ( is_wp_error( $rate_error ) ) {
            return $rate_error;
        }

        $to = $request->get_param( 'to' );

        if ( empty( $to ) ) {
            $to = get_option( 'admin_email' );
        }

        if ( ! is_email( $to ) ) {
            return new WP_Error(
                'invalid_email',
                __( 'Please provide a valid email address.', 'nimblymail-smtp' ),
                array( 'status' => 400 )
            );
        }

        // "Do Not Send" blocks all delivery, so a test would never arrive.
        // Report it clearly instead of attempting a send that reads as a failure.
        if ( (bool) Options::init()->get( 'general', 'do_not_send' ) ) {
            return new WP_REST_Response(
                array(
                    'success'   => false,
                    'debug_log' => '',
                    'message'   => __( 'Test email not sent: "Do Not Send" is enabled in Settings → Misc. Disable it to send email.', 'nimblymail-smtp' ),
                )
            );
        }

        // Developer Sandbox in Log Only mode captures the email without
        // delivering it, so a test would never arrive. Redirect mode still
        // sends one copy to the safe address, so it is allowed through.
        if ( Options::init()->get( 'sandbox', 'enabled' ) && 'log' === Options::init()->get( 'sandbox', 'mode' ) ) {
            return new WP_REST_Response(
                array(
                    'success'   => false,
                    'debug_log' => '',
                    'message'   => __( 'Test email not delivered: the Developer Sandbox is in Log Only mode. Switch it to Redirect or disable the Sandbox to receive a test email.', 'nimblymail-smtp' ),
                )
            );
        }

        if ( ! defined( 'NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED' ) || ! NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED ) {
            /**
             * Filters the list of permitted test email recipients.
             *
             * Return an array of email addresses that are allowed as the `to`
             * field for test email requests. Defaults to the site admin email.
             * Define NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED as true to bypass
             * this check entirely.
             *
             * @since 1.0.0
             *
             * @param string[] $allowed Allowed recipient addresses.
             *
             * @return string[]
             */
            $allowed = (array) apply_filters( 'nimblymail_test_email_recipients', array( get_option( 'admin_email' ) ) );

            if ( ! in_array( $to, $allowed, true ) ) {
                return new WP_Error(
                    'nimblymail_test_restricted_recipient',
                    __( 'Test emails can only be sent to permitted addresses. Use the nimblymail_test_email_recipients filter or NIMBLYMAIL_TEST_EMAIL_UNRESTRICTED constant to allow other recipients.', 'nimblymail-smtp' ),
                    array( 'status' => 403 )
                );
            }
        }

        $site_name = get_bloginfo( 'name' );

        $subject = sprintf(
            /* translators: %s: site name. */
            __( '[%s] Test Email - NimblyMail', 'nimblymail-smtp' ),
            $site_name
        );

        $body = $this->get_test_email_body( $site_name );

        $bypass_admin = (bool) Options::init()->get( 'queue', 'bypass_admin_emails' );

        $outcome = $this->send_with_debug_capture(
            function () use ( $to, $subject, $body, $bypass_admin ) {
                $headers = array( 'Content-Type: text/html' );
                // Test emails default to synchronous delivery so the admin
                // sees an immediate result. Operators who want to verify
                // the queue itself can flip the bypass setting off.
                if ( $bypass_admin ) {
                    Processor::mark_bypass(
                        array(
                            'to'      => $to,
                            'subject' => $subject,
                            'message' => $body,
                        )
                    );
                }
                return wp_mail( $to, $subject, $body, $headers );
            }
        );

        return new WP_REST_Response(
            array(
                'success'   => $outcome['success'],
                'debug_log' => $outcome['debug_log'],
                'message'   => $outcome['success']
                    ? __( 'Test email sent successfully!', 'nimblymail-smtp' )
                    : __( 'Failed to send test email.', 'nimblymail-smtp' ),
            )
        );
    }

    /**
     * Generate the HTML body for the test email.
     *
     * @since 1.0.0
     *
     * @param string $site_name The site name.
     *
     * @return string
     */
    private function get_test_email_body( string $site_name ): string {
        $site_url  = home_url();
        $timestamp = current_time( 'mysql' );

        return '<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
    <h2 style="color: #1d2327;">NimblyMail Test Email</h2>
    <p>This is a test email sent from <strong>' . esc_html( $site_name ) . '</strong>.</p>
    <p>Sent at: ' . esc_html( $timestamp ) . '</p>
    <p>Site URL: <a href="' . esc_url( $site_url ) . '">' . esc_html( $site_url ) . '</a></p>
    <hr style="border: none; border-top: 1px solid #dcdcde; margin: 20px 0;">
    <p style="color: #787c82; font-size: 13px;">If you received this email, your mail configuration is working correctly.</p>
</body>
</html>';
    }
}
