<?php

namespace Formatrica\Email;

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

use Formatrica\Forms\Form_Sanitizer;
use Formatrica\Submission\Rendering\Template_Renderer;
use Formatrica\Support\Secrets;

final class Email_Service
{
    private array $current_mail_message = [];

    /**
     * @var array<string, mixed>|null
     */
    private ?array $last_wp_mail_failure = null;

    /**
     * @var array<string, mixed>|null
     */
    private ?array $last_wp_mail_success = null;

    private bool $using_wordpress_mail = false;

    /**
     * @param array<string, mixed> $form
     * @param array<string, mixed> $submission
     * @param array<string, mixed> $context
     * @return array{sent: bool, message: string}
     */
    public function dispatch_submission(array $form, array $submission, array $context = []): array
    {
        $config = $form['config']['options'] ?? [];

        // Resolve email delivery: use global settings unless explicitly overridden per form
        $use_global = array_key_exists('use_global_email_delivery', $config)
            ? (bool) $config['use_global_email_delivery']
            : true;
        if ($use_global) {
            $global_delivery = $this->get_global_email_delivery();
            if (!empty($global_delivery)) {
                $config['email_delivery'] = $global_delivery;
            }
        }
        // Determine recipient: prefer global when flag set (default true)
        $use_global_recipient = array_key_exists('use_global_recipient', $config)
            ? (bool) $config['use_global_recipient']
            : true;
        if ($use_global_recipient) {
            $to = $this->get_global_default_recipient();
        } else {
            $to = !empty($config['email_recipient']) ? (string) $config['email_recipient'] : $this->get_global_default_recipient();
        }
        $subject = $config['email_subject'] ?? __('New form submission', 'formatrica');
        // Resolve {{form_title}} / {{field:name}} placeholders in the subject.
        $subject_field_meta = isset($context['fields']) && is_array($context['fields']) ? $context['fields'] : [];
        $subject = (new Template_Renderer())->render((string) $subject, $form, $submission, $subject_field_meta);
        if ($subject === '') {
            $subject = __('New form submission', 'formatrica');
        }
        $from_name = $config['email_from_name'] ?? get_bloginfo('name');
        $from_email = $config['email_from_address'] ?? $this->default_from_email();
        $provider = is_array($config['email_delivery'] ?? null)
            ? ($config['email_delivery']['provider'] ?? 'wordpress')
            : 'wordpress';

        // Optionally override From fields from global settings using granular flags (fallback to legacy flag)
        $global_email = $this->get_global_email_sender();
        $legacy_sender = array_key_exists('use_global_sender', $config) ? (bool) $config['use_global_sender'] : null;
        $use_global_from_name = array_key_exists('use_global_from_name', $config)
            ? (bool) $config['use_global_from_name']
            : ($legacy_sender !== null ? $legacy_sender : true);
        $use_global_from_email = array_key_exists('use_global_from_email', $config)
            ? (bool) $config['use_global_from_email']
            : ($legacy_sender !== null ? $legacy_sender : true);
        if ($use_global_from_name && !empty($global_email['from_name'])) {
            $from_name = (string) $global_email['from_name'];
        }
        if ($use_global_from_email && !empty($global_email['from_email'])) {
            $from_email = (string) $global_email['from_email'];
        }

        if (empty($to) || !is_email($to)) {
            return [
                'sent' => false,
                'message' => __('Recipient email is not configured.', 'formatrica'),
            ];
        }

        $fields = $context['fields'] ?? [];

        // Enforce sender domain only for WordPress/SMTP-based delivery.
        // API providers (SendGrid, Mailgun, Postmark, Brevo, SES, SMTP2GO) and Mailpit should use the exact configured From.
        $reply_to_override = null;
        if (in_array($provider, ['wordpress', 'smtp'], true)) {
            $sender_enforcement = $this->enforce_sender_domain($from_email);
            $from_email = $sender_enforcement['email'];
            $reply_to_override = $sender_enforcement['reply_to'] ?? null;
        }

        $headers = $this->build_headers($from_name, $from_email, $fields, $submission);
        if (!empty($reply_to_override) && !$this->header_has_reply_to($headers, $reply_to_override)) {
            $headers[] = sprintf('Reply-To: %s', $reply_to_override);
        }

        $body = $this->build_html_body($fields, $submission, $form, $context);
        $reply_to = $this->extract_reply_to($headers);

        $result = [
            'sent' => false,
            'message' => __('Email notifications disabled.', 'formatrica'),
        ];

        if (!empty($config['send_email'])) {
            $delivery = $this->deliver_email($config, [
                'to' => $to,
                'subject' => $subject,
                'html' => $body,
                'text' => $this->plain_text_body($body),
                'headers' => $headers,
                'from_name' => $from_name,
                'from_email' => $from_email,
                'reply_to' => $reply_to,
            ]);

            if (!$delivery['sent']) {
                set_transient('formatrica_email_warning', time(), DAY_IN_SECONDS);
            } else {
                delete_transient('formatrica_email_warning');
            }

            $result = [
                'sent' => $delivery['sent'],
                'message' => $delivery['sent']
                    ? __('Notification email sent.', 'formatrica')
                    : ($delivery['error'] ?? __('Email delivery failed.', 'formatrica')),
            ];
        }

        return $result;
    }


    /**
     * Resolve the effective notification From (name, email) for a form config,
     * honouring the global-sender flags. Shared by the notification path and the
     * submitter confirmation.
     *
     * @param array<string,mixed> $config
     * @return array{0:string,1:string} [from_name, from_email]
     */
    public function resolve_notification_sender(array $config): array
    {
        $from_name  = $config['email_from_name'] ?? get_bloginfo('name');
        $from_email = $config['email_from_address'] ?? $this->default_from_email();

        $global_email = $this->get_global_email_sender();
        $legacy       = array_key_exists('use_global_sender', $config) ? (bool) $config['use_global_sender'] : null;
        $use_gfn      = array_key_exists('use_global_from_name', $config) ? (bool) $config['use_global_from_name'] : ($legacy ?? true);
        $use_gfe      = array_key_exists('use_global_from_email', $config) ? (bool) $config['use_global_from_email'] : ($legacy ?? true);

        if ($use_gfn && !empty($global_email['from_name'])) {
            $from_name = (string) $global_email['from_name'];
        }
        if ($use_gfe && !empty($global_email['from_email'])) {
            $from_email = (string) $global_email['from_email'];
        }

        return [(string) $from_name, (string) $from_email];
    }




    public function send_test(string $recipient, array $options = []): array
    {
        $recipient = sanitize_email($recipient);

        if (empty($recipient) || !is_email($recipient)) {
            return [
                'sent' => false,
                'message' => __('The email address is invalid.', 'formatrica'),
            ];
        }

        $config = Form_Sanitizer::merge_with_defaults([
            'fields' => [],
            'options' => $options,
        ]);

        $opts = $config['options'];

        // Resolve global delivery if enabled
        $use_global_delivery = array_key_exists('use_global_email_delivery', $opts)
            ? (bool) $opts['use_global_email_delivery']
            : true;
        if ($use_global_delivery) {
            $global_delivery = $this->get_global_email_delivery();
            if (!empty($global_delivery)) {
                $opts['email_delivery'] = $global_delivery;
            }
        }

        if (empty($opts['send_email'])) {
            $opts['send_email'] = true;
        }

        $subject = __('Test email from Formatrica', 'formatrica');
        $from_name = $opts['email_from_name'] ?? get_bloginfo('name');
        $from_email = $opts['email_from_address'] ?? $this->default_from_email();

        // Optionally override From fields for test email using granular flags (fallback to legacy flag)
        $global_email = $this->get_global_email_sender();
        $legacy_sender = array_key_exists('use_global_sender', $opts) ? (bool) $opts['use_global_sender'] : null;
        $use_global_from_name = array_key_exists('use_global_from_name', $opts)
            ? (bool) $opts['use_global_from_name']
            : ($legacy_sender !== null ? $legacy_sender : true);
        $use_global_from_email = array_key_exists('use_global_from_email', $opts)
            ? (bool) $opts['use_global_from_email']
            : ($legacy_sender !== null ? $legacy_sender : true);
        if ($use_global_from_name && !empty($global_email['from_name'])) {
            $from_name = (string) $global_email['from_name'];
        }
        if ($use_global_from_email && !empty($global_email['from_email'])) {
            $from_email = (string) $global_email['from_email'];
        }
        $provider = is_array($opts['email_delivery'] ?? null)
            ? ($opts['email_delivery']['provider'] ?? 'wordpress')
            : 'wordpress';

        // Enforce sender domain only for WordPress/SMTP-based delivery
        if (in_array($provider, ['wordpress', 'smtp'], true)) {
            $sender = $this->enforce_sender_domain($from_email);
            $from_email = $sender['email'];
        }

        $headers = $this->build_headers($from_name, $from_email, [], []);
        $body = $this->build_html_body([
            [
                'name' => 'status',
                'label' => __('Status', 'formatrica'),
                'value' => __('Success! Emails are configured correctly.', 'formatrica'),
                'type' => 'text',
            ],
        ], [], [
            'title' => __('Test Form', 'formatrica'),
            'config' => ['options' => []],
        ], []);
        $reply_to = $this->extract_reply_to($headers);

        $delivery = $this->deliver_email($opts, [
            'to' => $recipient,
            'subject' => $subject,
            'html' => $body,
            'text' => $this->plain_text_body($body),
            'headers' => $headers,
            'from_name' => $from_name,
            'from_email' => $from_email,
            'reply_to' => $reply_to,
        ]);

        return [
            'sent' => $delivery['sent'],
            'message' => $delivery['sent']
                ? __('Test email dispatched. Check your inbox.', 'formatrica')
                : ($delivery['error'] ?? __('Sending test email failed.', 'formatrica')),
        ];
    }

    private function build_headers(string $from_name, string $from_email, array $fields, array $submission): array
    {
        $headers = [
            'Content-Type: text/html; charset=UTF-8',
            sprintf('From: %s <%s>', $from_name, $from_email),
        ];

        foreach ($fields as $field) {
            if (($field['type'] ?? '') === 'email' && !empty($field['value']) && is_string($field['value']) && filter_var($field['value'], FILTER_VALIDATE_EMAIL)) {
                $headers[] = sprintf('Reply-To: %s', $field['value']);
                break;
            }
        }

        if (empty($fields)) {
            foreach ($submission as $key => $value) {
                if (!is_string($value)) {
                    continue;
                }

                if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
                    $headers[] = sprintf('Reply-To: %s', $value);
                    break;
                }
            }
        }

        return $headers;
    }

    private function build_html_body(array $fields, array $submission, array $form, array $context): string
    {
        // Email template controls (Pro emails.template_controls) come from the
        // Pro package via this filter (Phase-5 completion). Free always renders
        // the defaults below - a Pro-authored template applies only where the
        // Pro package is present (capability-blind there per the runtime policy).
        $options = $form['config']['options'] ?? [];
        $tpl = (array) apply_filters('formatrica_email_template_style', [
            'accent'        => '#1E6EC8',
            'font'          => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
            'show_title'    => true,
            'show_metadata' => true,
            'intro_text'    => '',
            'footer_text'   => '',
        ], $options);
        $accent        = (string) ($tpl['accent'] ?? '#1E6EC8');
        $font          = (string) ($tpl['font'] ?? 'sans-serif');
        $show_title    = !empty($tpl['show_title']);
        $show_metadata = !empty($tpl['show_metadata']);
        $intro_text    = trim((string) ($tpl['intro_text'] ?? ''));
        $footer_text   = trim((string) ($tpl['footer_text'] ?? ''));

        $rows = '';

        if (!empty($fields)) {
            foreach ($fields as $field) {
                $label = esc_html($field['label'] ?? $field['name'] ?? '');
                $value = $field['value'];

                // Edition display override (Pro registers richtext HTML +
                // multi-file link rendering). A string return is trusted,
                // pre-escaped HTML for the value cell.
                $override = apply_filters('formatrica_email_field_display_html', null, $field);
                if (is_string($override)) {
                    $rows .= sprintf('<tr><th align="left" style="padding:8px 12px;border-bottom:1px solid #eee;vertical-align:top;">%s</th><td style="padding:8px 12px;border-bottom:1px solid #eee;">%s</td></tr>', $label, $override);
                    continue;
                }

                $display = is_array($value)
                    ? implode(', ', array_map('esc_html', array_map('strval', $value)))
                    : esc_html((string) $value);

                if (!empty($field['meta']['url']) && is_string($field['meta']['url'])) {
                    $link_text = !empty($field['meta']['original_name'])
                        ? esc_html($field['meta']['original_name'])
                        : $display;

                    $display = sprintf(
                        '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
                        esc_url($field['meta']['url']),
                        $link_text,
                    );
                }

                $rows .= sprintf('<tr><th align="left" style="padding:8px 12px;border-bottom:1px solid #eee;">%s</th><td style="padding:8px 12px;border-bottom:1px solid #eee;">%s</td></tr>', $label, $display);
            }
        } else {
            foreach ($submission as $key => $value) {
                $label = esc_html(is_string($key) ? $key : (string) $key);
                $display = is_array($value)
                    ? implode(', ', array_map('esc_html', array_map('strval', $value)))
                    : esc_html((string) $value);

                $rows .= sprintf('<tr><th align="left" style="padding:8px 12px;border-bottom:1px solid #eee;">%s</th><td style="padding:8px 12px;border-bottom:1px solid #eee;">%s</td></tr>', $label, $display);
            }
        }

        $title_html = '';
        if ($show_title) {
            $title = (string) ($form['title'] ?? '');
            if ($title !== '') {
                $title_html = sprintf(
                    '<h2 style="color:%s;font-size:20px;margin:0 0 12px 0;">%s</h2>',
                    esc_attr($accent),
                    esc_html($title)
                );
            }
        }

        $intro_html = $intro_text !== ''
            ? sprintf('<p style="margin:0 0 14px 0;">%s</p>', nl2br(esc_html($intro_text)))
            : '';

        $table = sprintf(
            '<table width="100%%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1d2327;border-top:3px solid %s;">%s</table>',
            esc_attr($accent),
            $rows
        );

        $meta = '';
        if ($show_metadata && !empty($context['ip_address'])) {
            $meta .= sprintf('<p style="margin:12px 0 0 0;font-size:12px;color:#6c757d;">IP: %s</p>', esc_html($context['ip_address']));
        }
        if ($show_metadata && !empty($context['user_agent'])) {
            $meta .= sprintf('<p style="margin:2px 0 0 0;font-size:12px;color:#6c757d;">%s</p>', esc_html((string) $context['user_agent']));
        }

        $footer_html = $footer_text !== ''
            ? sprintf('<p style="margin:16px 0 0 0;font-size:12px;color:#6c757d;">%s</p>', nl2br(esc_html($footer_text)))
            : '';

        return sprintf(
            '<div style="font-family:%s;">%s%s%s%s%s</div>',
            esc_attr($font),
            $title_html,
            $intro_html,
            $table,
            $meta,
            $footer_html
        );
    }

    /**
     * @param array<string, mixed> $options
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    public function deliver_email(array $options, array $message): array
    {
        $delivery = $options['email_delivery'] ?? [];
        $provider = $delivery['provider'] ?? 'wordpress';
        $reply_to = $message['reply_to'] ?? null;

        switch ($provider) {
            case 'sendgrid':
                return $this->send_via_sendgrid($delivery['sendgrid'] ?? [], $message, $reply_to);
            case 'mailgun':
                return $this->send_via_mailgun($delivery['mailgun'] ?? [], $message, $reply_to);
            case 'postmark':
                return $this->send_via_postmark($delivery['postmark'] ?? [], $message, $reply_to);
            case 'brevo':
                return $this->send_via_brevo($delivery['brevo'] ?? [], $message, $reply_to);
            case 'amazon_ses':
                return $this->send_via_amazon_ses($delivery['amazon_ses'] ?? [], $message, $reply_to);
            case 'mailpit':
                return $this->send_via_smtp([
                    'host' => $delivery['mailpit']['host'] ?? '127.0.0.1',
                    'port' => $delivery['mailpit']['port'] ?? 1025,
                    'encryption' => 'none',
                    'username' => '',
                    'password' => '',
                ], $message, $reply_to);
            case 'smtp2go':
                return $this->send_via_smtp2go($delivery['smtp2go'] ?? [], $message, $reply_to);
            case 'smtp':
                $password = $delivery['smtp']['password'] ?? '';
                $password = $password !== '' ? Secrets::decrypt($password) : '';

                return $this->send_via_smtp([
                    'host' => $delivery['smtp']['host'] ?? '',
                    'port' => $delivery['smtp']['port'] ?? 587,
                    'encryption' => $delivery['smtp']['encryption'] ?? 'tls',
                    'username' => $delivery['smtp']['username'] ?? '',
                    'password' => $password,
                ], $message, $reply_to);
            default:
                return $this->send_via_wordpress($message);
        }
    }

    /**
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_wordpress(array $message): array
    {
        $this->using_wordpress_mail = true;
        $this->current_mail_message = $message;
        $this->last_wp_mail_failure = null;
        $this->last_wp_mail_success = null;

        add_action('phpmailer_init', [$this, 'handle_phpmailer_init']);
        add_action('wp_mail_failed', [$this, 'handle_wp_mail_failed']);
        add_action('wp_mail_succeeded', [$this, 'handle_wp_mail_succeeded']);

        try {
            $sent = wp_mail($message['to'], $message['subject'], $message['html'], $message['headers']);
        } finally {
            remove_action('phpmailer_init', [$this, 'handle_phpmailer_init']);
            remove_action('wp_mail_failed', [$this, 'handle_wp_mail_failed']);
            remove_action('wp_mail_succeeded', [$this, 'handle_wp_mail_succeeded']);
            $this->using_wordpress_mail = false;
            $this->current_mail_message = [];
        }

        if ($this->last_wp_mail_failure !== null) {
            $details = $this->last_wp_mail_failure;
            $details['recorded_at'] = time();
            set_transient('formatrica_wp_mail_error_details', $details, DAY_IN_SECONDS);
        } elseif ($this->last_wp_mail_success !== null) {
            delete_transient('formatrica_wp_mail_error_details');
            set_transient('formatrica_wp_mail_last_success', $this->last_wp_mail_success, DAY_IN_SECONDS);
        }

        return [
            'sent' => (bool) $sent,
            'error' => $sent ? null : __('Email delivery failed.', 'formatrica'),
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_sendgrid(array $settings, array $message, ?string $reply_to): array
    {
        $api_key = Secrets::reveal(isset($settings['api_key']) ? trim((string) $settings['api_key']) : '');

        if ($api_key === '') {
            return [
                'sent' => false,
                'error' => __('SendGrid API key is missing.', 'formatrica'),
            ];
        }

        $payload = [
            'personalizations' => [
                [
                    'to' => [['email' => $message['to']]],
                ],
            ],
            'from' => [
                'email' => $message['from_email'],
                'name' => $message['from_name'],
            ],
            'subject' => $message['subject'],
            'content' => [
                [
                    'type' => 'text/html',
                    'value' => $message['html'],
                ],
                [
                    'type' => 'text/plain',
                    'value' => $message['text'],
                ],
            ],
        ];

        if ($reply_to) {
            $payload['reply_to'] = ['email' => $reply_to];
        }

        $response = wp_remote_post('https://api.sendgrid.com/v3/mail/send', [
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        if ($code === 202) {
            return ['sent' => true, 'error' => null];
        }

        return [
            'sent' => false,
            /* translators: %d: HTTP status code */
            'error' => sprintf(__('SendGrid API responded with status %d.', 'formatrica'), $code ?: 0),
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_smtp2go(array $settings, array $message, ?string $reply_to): array
    {
        $api_key = Secrets::reveal(isset($settings['api_key']) ? trim((string) $settings['api_key']) : '');

        if ($api_key === '') {
            return [
                'sent' => false,
                'error' => __('SMTP2GO API key is missing.', 'formatrica'),
            ];
        }

        $payload = [
            'api_key' => $api_key,
            'to' => [$message['to']],
            'sender' => $message['from_email'],
            'subject' => $message['subject'],
            'html_body' => $message['html'],
            'text_body' => $message['text'],
        ];

        if ($reply_to) {
            $payload['reply_to'] = $reply_to;
        }

        $response = wp_remote_post('https://api.smtp2go.com/v3/email/send', [
            'headers' => [
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        if ($code !== 200) {
            return [
                'sent' => false,
                /* translators: %d: HTTP status code */
                'error' => sprintf(__('SMTP2GO API responded with status %d.', 'formatrica'), $code ?: 0),
            ];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $succeeded = (int) ($body['data']['succeeded'] ?? 0);

        if ($succeeded >= 1) {
            return ['sent' => true, 'error' => null];
        }

        $error = $body['data']['error'] ?? __('Email delivery failed.', 'formatrica');

        return [
            'sent' => false,
            'error' => is_string($error) ? $error : __('Email delivery failed.', 'formatrica'),
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_mailgun(array $settings, array $message, ?string $reply_to): array
    {
        $api_key = Secrets::reveal(isset($settings['api_key']) ? trim((string) $settings['api_key']) : '');
        $domain = isset($settings['domain']) ? trim((string) $settings['domain']) : '';

        if ($api_key === '') {
            return [
                'sent' => false,
                'error' => __('Mailgun API key is missing.', 'formatrica'),
            ];
        }

        if ($domain === '') {
            return [
                'sent' => false,
                'error' => __('Mailgun domain is missing.', 'formatrica'),
            ];
        }

        $region = isset($settings['region']) ? trim((string) $settings['region']) : 'us';
        $api_url = $region === 'eu'
            ? 'https://api.eu.mailgun.net/v3/'
            : 'https://api.mailgun.net/v3/';

        $payload = [
            'from' => sprintf('%s <%s>', $message['from_name'], $message['from_email']),
            'to' => $message['to'],
            'subject' => $message['subject'],
            'html' => $message['html'],
            'text' => $message['text'],
        ];

        if ($reply_to) {
            $payload['h:Reply-To'] = $reply_to;
        }

        $endpoint = $api_url . $domain . '/messages';
        $response = wp_remote_post($endpoint, [
            'headers' => [
                'Authorization' => 'Basic ' . base64_encode('api:' . $api_key),
            ],
            'body' => $payload,
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            do_action('formatrica_debug_log', '[Formatrica Email] Mailgun request error: ' . $response->get_error_message());
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        if ($code === 200) {
            return ['sent' => true, 'error' => null];
        }

        $body = wp_remote_retrieve_body($response);
        do_action('formatrica_debug_log', sprintf('[Formatrica Email] Mailgun API error: HTTP %d; endpoint=%s; body=%s', $code ?: 0, $endpoint, is_string($body) ? $body : ''));

        /* translators: %d: HTTP status code */
        $error_msg = sprintf(__('Mailgun API responded with status %d.', 'formatrica'), $code ?: 0);
        return [
            'sent' => false,
            'error' => $error_msg,
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_postmark(array $settings, array $message, ?string $reply_to): array
    {
        $api_token = Secrets::reveal(isset($settings['api_token']) ? trim((string) $settings['api_token']) : '');

        if ($api_token === '') {
            return [
                'sent' => false,
                'error' => __('Postmark API token is missing.', 'formatrica'),
            ];
        }

        $payload = [
            'From' => sprintf('%s <%s>', $message['from_name'], $message['from_email']),
            'To' => $message['to'],
            'Subject' => $message['subject'],
            'HtmlBody' => $message['html'],
            'TextBody' => $message['text'],
            'MessageStream' => 'outbound',
        ];

        if ($reply_to) {
            $payload['ReplyTo'] = $reply_to;
        }

        $response = wp_remote_post('https://api.postmarkapp.com/email', [
            'headers' => [
                'X-Postmark-Server-Token' => $api_token,
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            do_action('formatrica_debug_log', '[Formatrica Email] Postmark request error: ' . $response->get_error_message());
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        $body_raw = wp_remote_retrieve_body($response);
        $body = json_decode($body_raw, true);

        if ($code === 200) {
            do_action('formatrica_debug_log', sprintf('[Formatrica Email] Postmark success: HTTP %d; body=%s', $code, is_string($body_raw) ? $body_raw : ''));
            return ['sent' => true, 'error' => null];
        }

        // Try to decode Postmark error details
        /* translators: %d: HTTP status code */
        $error_message = sprintf(__('Postmark API responded with status %d.', 'formatrica'), $code ?: 0);
        if (is_array($body)) {
            if (!empty($body['Message']) && is_string($body['Message'])) {
                $error_message = $body['Message'];
            } elseif (!empty($body['ErrorCode'])) {
                $error_message = sprintf('Postmark error %s: %s', $body['ErrorCode'], $body['Message'] ?? $error_message);
            }
        }

        do_action('formatrica_debug_log', sprintf('[Formatrica Email] Postmark API error: HTTP %d; body=%s', $code ?: 0, is_string($body_raw) ? $body_raw : ''));

        return [
            'sent' => false,
            'error' => $error_message,
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_brevo(array $settings, array $message, ?string $reply_to): array
    {
        $api_key = Secrets::reveal(isset($settings['api_key']) ? trim((string) $settings['api_key']) : '');

        if ($api_key === '') {
            return [
                'sent' => false,
                'error' => __('Brevo API key is missing.', 'formatrica'),
            ];
        }

        $payload = [
            'sender' => [
                'name' => $message['from_name'],
                'email' => $message['from_email'],
            ],
            'to' => [
                ['email' => $message['to']],
            ],
            'subject' => $message['subject'],
            'htmlContent' => $message['html'],
            'textContent' => $message['text'],
        ];

        if ($reply_to) {
            $payload['replyTo'] = ['email' => $reply_to];
        }

        $response = wp_remote_post('https://api.brevo.com/v3/smtp/email', [
            'headers' => [
                'api-key' => $api_key,
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        $body_raw = wp_remote_retrieve_body($response);

        if (is_wp_error($response)) {
            do_action('formatrica_debug_log', '[Formatrica Email] Brevo request error: ' . $response->get_error_message());
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = json_decode($body_raw, true);

        if ($code === 201) {
            do_action('formatrica_debug_log', sprintf('[Formatrica Email] Brevo success: HTTP %d; body=%s', $code, is_string($body_raw) ? $body_raw : ''));
            return ['sent' => true, 'error' => null];
        }

        /* translators: %d: HTTP status code */
        $error_message = sprintf(__('Brevo API responded with status %d.', 'formatrica'), $code ?: 0);
        if (is_array($body) && !empty($body['message']) && is_string($body['message'])) {
            $error_message = $body['message'];
        }

        do_action('formatrica_debug_log', sprintf('[Formatrica Email] Brevo API error: HTTP %d; body=%s', $code ?: 0, is_string($body_raw) ? $body_raw : ''));

        return [
            'sent' => false,
            'error' => $error_message,
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_amazon_ses(array $settings, array $message, ?string $reply_to): array
    {
        $access_key = isset($settings['access_key']) ? trim((string) $settings['access_key']) : '';
        $secret_key = Secrets::reveal(isset($settings['secret_key']) ? trim((string) $settings['secret_key']) : '');
        $region = isset($settings['region']) ? trim((string) $settings['region']) : 'us-east-1';

        if ($access_key === '') {
            return [
                'sent' => false,
                'error' => __('Amazon SES access key is missing.', 'formatrica'),
            ];
        }

        if ($secret_key === '') {
            return [
                'sent' => false,
                'error' => __('Amazon SES secret key is missing.', 'formatrica'),
            ];
        }

        $endpoint = 'https://email.' . $region . '.amazonaws.com';
        $service = 'ses';
        $host = 'email.' . $region . '.amazonaws.com';
        $method = 'POST';
        $content_type = 'application/x-www-form-urlencoded';

        // Prepare the email payload as AWS SES expects
        $params = [
            'Action' => 'SendEmail',
            'Source' => sprintf('%s <%s>', $message['from_name'], $message['from_email']),
            'Destination.ToAddresses.member.1' => $message['to'],
            'Message.Subject.Data' => $message['subject'],
            'Message.Subject.Charset' => 'UTF-8',
            'Message.Body.Html.Data' => $message['html'],
            'Message.Body.Html.Charset' => 'UTF-8',
            'Message.Body.Text.Data' => $message['text'],
            'Message.Body.Text.Charset' => 'UTF-8',
        ];

        if ($reply_to) {
            $params['ReplyToAddresses.member.1'] = $reply_to;
        }

        $payload = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
        $payload_hash = hash('sha256', $payload);

        // Create AWS Signature Version 4
        $date = gmdate('Ymd');
        $datetime = gmdate('Ymd\THis\Z');
        $credential_scope = $date . '/' . $region . '/' . $service . '/aws4_request';

        $canonical_request = implode("\n", [
            $method,
            '/',
            '',
            'content-type:' . $content_type,
            'host:' . $host,
            'x-amz-date:' . $datetime,
            '',
            'content-type;host;x-amz-date',
            $payload_hash,
        ]);

        $string_to_sign = implode("\n", [
            'AWS4-HMAC-SHA256',
            $datetime,
            $credential_scope,
            hash('sha256', $canonical_request),
        ]);

        $k_date = hash_hmac('sha256', $date, 'AWS4' . $secret_key, true);
        $k_region = hash_hmac('sha256', $region, $k_date, true);
        $k_service = hash_hmac('sha256', $service, $k_region, true);
        $k_signing = hash_hmac('sha256', 'aws4_request', $k_service, true);
        $signature = hash_hmac('sha256', $string_to_sign, $k_signing);

        $authorization = 'AWS4-HMAC-SHA256 ' .
            'Credential=' . $access_key . '/' . $credential_scope . ', ' .
            'SignedHeaders=content-type;host;x-amz-date, ' .
            'Signature=' . $signature;

        $response = wp_remote_post($endpoint, [
            'headers' => [
                'Content-Type' => $content_type,
                'Host' => $host,
                'X-Amz-Date' => $datetime,
                'Authorization' => $authorization,
            ],
            'body' => $payload,
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return [
                'sent' => false,
                'error' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        if ($code === 200) {
            return ['sent' => true, 'error' => null];
        }

        $body = wp_remote_retrieve_body($response);
        /* translators: %d: HTTP status code */
        $error_message = sprintf(__('Amazon SES responded with status %d.', 'formatrica'), $code ?: 0);

        // Try to extract error message from XML response
        if (strpos($body, '<Message>') !== false && preg_match('/<Message>(.*?)<\/Message>/', $body, $matches)) {
            $error_message .= ' ' . $matches[1];
        }

        return [
            'sent' => false,
            'error' => $error_message,
        ];
    }

    /**
     * @param array<string, mixed> $settings
     * @param array<string, mixed> $message
     * @return array{sent: bool, error: string|null}
     */
    private function send_via_smtp(array $settings, array $message, ?string $reply_to): array
    {
        if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
            $base = ABSPATH . WPINC . '/PHPMailer/';
            if (file_exists($base . 'PHPMailer.php')) {
                require_once $base . 'PHPMailer.php';
                require_once $base . 'SMTP.php';
                require_once $base . 'Exception.php';
            }
        }

        if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
            return [
                'sent' => false,
                'error' => __('PHPMailer library is not available.', 'formatrica'),
            ];
        }

        $phpmailer_class = '\\PHPMailer\\PHPMailer\\PHPMailer';
        $phpmailer_exception = '\\PHPMailer\\PHPMailer\\Exception';

        $host = isset($settings['host']) ? trim((string) $settings['host']) : '';

        if ($host === '') {
            return [
                'sent' => false,
                'error' => __('SMTP host is not configured.', 'formatrica'),
            ];
        }

        $port = (int) ($settings['port'] ?? 587);
        $encryption = $settings['encryption'] ?? 'tls';
        $username = isset($settings['username']) ? (string) $settings['username'] : '';
        $password = isset($settings['password']) ? (string) $settings['password'] : '';
        $use_auth = $username !== '' || $password !== '';

        $mailer = new $phpmailer_class(true);

        try {
            $mailer->isSMTP();
            $mailer->Host = $host;
            $mailer->Port = $port;
            $mailer->SMTPAuth = $use_auth;

            if ($use_auth) {
                $mailer->Username = $username;
                $mailer->Password = $password;
            }

            if ($encryption && $encryption !== 'none') {
                $mailer->SMTPSecure = $encryption;
            }

            $mailer->CharSet = 'UTF-8';
            $mailer->setFrom($message['from_email'], $message['from_name']);
            $mailer->addAddress($message['to']);

            if ($reply_to) {
                $mailer->addReplyTo($reply_to);
            }

            $mailer->Subject = $message['subject'];
            $mailer->Body = $message['html'];
            $mailer->AltBody = $message['text'];
            $mailer->isHTML(true);

            $mailer->send();

            return ['sent' => true, 'error' => null];
        } catch (\Exception $exception) {
            return [
                'sent' => false,
                'error' => $exception->getMessage(),
            ];
        }
    }

    public function plain_text_body(string $html): string
    {
        return trim(wp_strip_all_tags($html));
    }

    private function extract_reply_to(array $headers): ?string
    {
        foreach ($headers as $header) {
            if (stripos($header, 'Reply-To:') === 0) {
                $value = trim(substr($header, strlen('Reply-To:')));
                if ($value !== '') {
                    return $value;
                }
            }
        }

        return null;
    }

    /**
     * @return array{email: string, reply_to: string|null}
     */
    public function enforce_sender_domain(string $from_email): array
    {
        $default_email = $this->default_from_email();
        $site_domain = $this->extract_domain($default_email);
        $from_email = trim($from_email);

        if ($from_email === '') {
            delete_transient('formatrica_email_sender_warning');
            return [
                'email' => $default_email,
                'reply_to' => null,
            ];
        }

        if (!is_email($from_email)) {
            set_transient('formatrica_email_sender_warning', [
                'reason' => 'invalid',
                'attempted' => $from_email,
                'expected_domain' => $site_domain,
            ], DAY_IN_SECONDS);

            return [
                'email' => $default_email,
                'reply_to' => null,
            ];
        }

        $email_domain = $this->extract_domain($from_email);

        if ($email_domain !== '' && $site_domain !== '' && $this->domains_match($email_domain, $site_domain)) {
            delete_transient('formatrica_email_sender_warning');

            return [
                'email' => $from_email,
                'reply_to' => null,
            ];
        }

        set_transient('formatrica_email_sender_warning', [
            'reason' => 'mismatch',
            'attempted' => $from_email,
            'actual_domain' => $email_domain,
            'expected_domain' => $site_domain,
        ], DAY_IN_SECONDS);

        return [
            'email' => $default_email,
            'reply_to' => $from_email,
        ];
    }

    private function extract_domain(string $email): string
    {
        $parts = explode('@', $email);
        $domain = $parts[1] ?? '';
        $domain = strtolower(trim($domain));

        if ($domain === '') {
            return '';
        }

        return preg_replace('/^www\./', '', $domain) ?? $domain;
    }

    private function domains_match(string $email_domain, string $site_domain): bool
    {
        if ($email_domain === '' || $site_domain === '') {
            return false;
        }

        if ($email_domain === $site_domain) {
            return true;
        }

        return str_ends_with($email_domain, '.' . $site_domain);
    }

    private function header_has_reply_to(array $headers, string $address): bool
    {
        foreach ($headers as $header) {
            if (stripos($header, 'Reply-To:') === 0) {
                $value = trim(substr($header, strlen('Reply-To:')));
                if (strcasecmp($value, $address) === 0) {
                    return true;
                }
            }
        }

        return false;
    }

    private function default_from_email(): string
    {
        $domain = wp_parse_url(home_url(), PHP_URL_HOST);

        if (!is_string($domain) || empty($domain)) {
            $domain = 'example.com';
        }

        return 'noreply@' . preg_replace('/^www\./', '', $domain);
    }

    /**
     * Load global email delivery settings from wp_options.
     *
     * @return array<string,mixed>
     */
    public function get_global_email_delivery(): array
    {
        $settings = get_option('formatrica_settings', []);
        if (!is_array($settings)) {
            return [];
        }
        $email = $settings['email'] ?? [];
        if (!is_array($email)) {
            return [];
        }
        $delivery = $email['delivery'] ?? [];
        return is_array($delivery) ? $delivery : [];
    }

    /**
     * Load global from_name/from_email settings from wp_options.
     *
     * @return array{from_name?:string, from_email?:string}
     */
    private function get_global_email_sender(): array
    {
        $settings = get_option('formatrica_settings', []);
        if (!is_array($settings)) {
            return [];
        }
        $email = $settings['email'] ?? [];
        return is_array($email) ? [
            'from_name' => isset($email['from_name']) ? (string) $email['from_name'] : '',
            'from_email' => isset($email['from_email']) ? (string) $email['from_email'] : '',
        ] : [];
    }

    /**
     * Public accessor for the global default recipient.
     */
    public function resolve_global_recipient(): string
    {
        return $this->get_global_default_recipient();
    }

    /**
     * Get the global default recipient email, falling back to WordPress admin email.
     */
    private function get_global_default_recipient(): string
    {
        $settings = get_option('formatrica_settings', []);
        if (!is_array($settings)) {
            return (string) get_option('admin_email');
        }
        $email = $settings['email'] ?? [];
        $recipient = is_array($email) && isset($email['recipient']) ? (string) $email['recipient'] : '';
        if ($recipient !== '' && is_email($recipient)) {
            return $recipient;
        }
        return (string) get_option('admin_email');
    }

    /**
     * Send a generic email using the same delivery options as submissions.
     *
     * @param array<string, mixed> $options Form-level options (expects the same shape as form config options)
     * @param array<string, mixed> $args {to, subject, html, text?, from_name?, from_email?, reply_to?}
     * @return array{sent: bool, error: string|null}
     */
    public function send_generic(array $options, array $args): array
    {
        // Resolve email delivery the same way dispatch_submission() does:
        // global settings unless the form explicitly overrides them.
        $use_global = array_key_exists('use_global_email_delivery', $options)
            ? (bool) $options['use_global_email_delivery']
            : true;
        if ($use_global) {
            $global_delivery = $this->get_global_email_delivery();
            if (!empty($global_delivery)) {
                $options['email_delivery'] = $global_delivery;
            }
        }

        $to = isset($args['to']) ? (string) $args['to'] : '';
        $subject = isset($args['subject']) ? (string) $args['subject'] : '';
        $html = isset($args['html']) ? (string) $args['html'] : '';
        $text = isset($args['text']) ? (string) $args['text'] : '';
        $from_name = isset($args['from_name']) ? (string) $args['from_name'] : (string) get_bloginfo('name');
        $from_email = isset($args['from_email']) ? (string) $args['from_email'] : $this->default_from_email();
        $reply_to = isset($args['reply_to']) ? (string) $args['reply_to'] : null;

        if ($to === '' || !is_email($to)) {
            return ['sent' => false, 'error' => __('Recipient email is not configured.', 'formatrica')];
        }

        // Enforce sender domain only for WordPress/SMTP-based delivery.
        $provider = is_array($options['email_delivery'] ?? null)
            ? ($options['email_delivery']['provider'] ?? 'wordpress')
            : 'wordpress';
        if (in_array($provider, ['wordpress', 'smtp'], true)) {
            $sender = $this->enforce_sender_domain($from_email);
            $from_email = $sender['email'];
            if (!$reply_to && !empty($sender['reply_to'])) {
                $reply_to = $sender['reply_to'];
            }
        }

        $headers = [
            'Content-Type: text/html; charset=UTF-8',
            sprintf('From: %s <%s>', $from_name, $from_email),
        ];

        if ($reply_to) {
            $headers[] = sprintf('Reply-To: %s', $reply_to);
        }

        if ($text === '') {
            $text = $this->plain_text_body($html);
        }

        $payload = [
            'to' => $to,
            'subject' => $subject,
            'html' => $html,
            'text' => $text,
            'headers' => $headers,
            'from_name' => $from_name,
            'from_email' => $from_email,
            'reply_to' => $reply_to,
        ];

        return $this->deliver_email($options, $payload);
    }

    /**
     * Ensure PHPMailer is configured with UTF-8, HTML + text bodies, and a valid Sender.
     */
    public function handle_phpmailer_init($phpmailer): void
    {
        if (!$this->using_wordpress_mail || empty($this->current_mail_message)) {
            return;
        }

        if (!is_object($phpmailer)) {
            return;
        }

        if (isset($this->current_mail_message['from_email']) && is_email($this->current_mail_message['from_email'] ?? '')) {
            $from_name = $this->current_mail_message['from_name'] ?? '';
            $from_email = $this->current_mail_message['from_email'];

            if (method_exists($phpmailer, 'setFrom')) {
                $phpmailer->setFrom($from_email, $from_name, false);
            }

            if (property_exists($phpmailer, 'Sender')) {
                $phpmailer->Sender = $from_email;
            }
        }

        if (isset($this->current_mail_message['reply_to']) && is_email($this->current_mail_message['reply_to'])) {
            $reply_to = $this->current_mail_message['reply_to'];
            if (method_exists($phpmailer, 'addReplyTo')) {
                // @phpstan-ignore-next-line PHPMailer method signature
                $phpmailer->addReplyTo($reply_to);
            }
        }

        if (!empty($this->current_mail_message['text']) && property_exists($phpmailer, 'AltBody')) {
            $phpmailer->AltBody = (string) $this->current_mail_message['text'];
        }

        if (property_exists($phpmailer, 'CharSet')) {
            $phpmailer->CharSet = 'UTF-8';
        }

        if (method_exists($phpmailer, 'isHTML')) {
            $phpmailer->isHTML(true);
        }
    }

    /**
     * Capture detailed error information when wp_mail() fails.
     */
    public function handle_wp_mail_failed($error): void
    {
        if (!$this->using_wordpress_mail || !($error instanceof \WP_Error)) {
            return;
        }

        $data = $error->get_error_data();
        $this->last_wp_mail_failure = [
            'message' => $error->get_error_message(),
            'code' => $error->get_error_code(),
            'details' => $this->condense_mail_error_data($data),
        ];
    }

    /**
     * Store metadata for successful deliveries so we can surface it in admin notices.
     *
     * @param array<string, mixed> $mail_data
     */
    public function handle_wp_mail_succeeded(array $mail_data): void
    {
        if (!$this->using_wordpress_mail) {
            return;
        }

        $to = $mail_data['to'] ?? [];
        if (is_string($to)) {
            $recipients = $to;
        } elseif (is_array($to)) {
            $flattened = array_map(static function ($recipient) {
                return is_string($recipient) ? $recipient : '';
            }, $to);
            $recipients = implode(', ', array_filter($flattened));
        } else {
            $recipients = '';
        }

        $this->last_wp_mail_success = [
            'to' => $recipients,
            'subject' => is_string($mail_data['subject'] ?? '') ? $mail_data['subject'] : '',
            'recorded_at' => time(),
        ];
    }

    /**
     * @param mixed $data
     * @return array<string, string>
     */
    private function condense_mail_error_data($data): array
    {
        if (!is_array($data)) {
            return [];
        }

        $details = [];

        if (!empty($data['phpmailer_exception_message']) && is_string($data['phpmailer_exception_message'])) {
            $details['phpmailer_exception_message'] = $data['phpmailer_exception_message'];
        }

        if (!empty($data['phpmailer_exception_code']) && (is_string($data['phpmailer_exception_code']) || is_int($data['phpmailer_exception_code']))) {
            $details['phpmailer_exception_code'] = (string) $data['phpmailer_exception_code'];
        }

        if (!empty($data['mail']) && is_array($data['mail'])) {
            $mail = $data['mail'];
            if (!empty($mail['to'])) {
                if (is_array($mail['to'])) {
                    $details['to'] = implode(', ', array_filter(array_map('strval', $mail['to'])));
                } elseif (is_string($mail['to'])) {
                    $details['to'] = $mail['to'];
                }
            }

            if (!empty($mail['subject']) && is_string($mail['subject'])) {
                $details['subject'] = $mail['subject'];
            }
        }

        return $details;
    }
}
