<?php

declare(strict_types=1);

namespace FecaMailshots\Admin;

use FecaMailshots\Application\MailshotRunService;
use FecaMailshots\Application\MailshotService;
use FecaMailshots\WordPress\WordPressFacade;

final class MailshotTestAdminPage
{
    use AdminRequestHelpers;

    private const RESULT_OPTION_KEY = 'feca_mailshots_test_ui_result';
    private const CAPABILITY = 'edit_pages';
    private const NONCE_ACTION = 'feca_mailshots_test';
    private const DOWNLOAD_MEMORY_LIMIT_ENV = 'FECA_MAILSHOTS_DOWNLOAD_MEMORY_LIMIT';
    private const DOWNLOAD_MEMORY_LIMIT_OPTION = 'feca_mailshots_download_memory_limit';

    /** @var callable(): MailshotRunService */
    private $runServiceFactory;

    /** @var callable(): MailshotService */
    private $mailshotServiceFactory;

    private WordPressFacade $wp;

    /** @param callable(): MailshotRunService $runServiceFactory @param callable(): MailshotService $mailshotServiceFactory */
    public function __construct(callable $runServiceFactory, callable $mailshotServiceFactory, WordPressFacade $wp)
    {
        $this->runServiceFactory = $runServiceFactory;
        $this->mailshotServiceFactory = $mailshotServiceFactory;
        $this->wp = $wp;
    }

    public function register(): void
    {
        $this->wp->addAction('admin_menu', [$this, 'registerMenu']);
        $this->wp->addAction('admin_post_feca_mailshots_test_api', [$this, 'handleApi']);
        $this->wp->addAction('admin_post_feca_mailshots_test_render_ui', [$this, 'handleRenderUi']);
        $this->wp->addAction('admin_post_feca_mailshots_test_send_ui', [$this, 'handleSendUi']);
    }

    public function registerMenu(): void
    {
        $this->wp->addSubmenuPage('feca-mailshot', 'Mailshot Test', 'Mailshot Test', self::CAPABILITY, 'feca-mailshots-test', [$this, 'render']);
    }

    public function render(): void
    {
        if (!$this->wp->currentUserCan(self::CAPABILITY)) {
            echo 'Permission denied';
            return;
        }

        $mailshots = $this->mailshotService()->list();
        $selectedMailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
        if ($selectedMailshotId <= 0 && $mailshots !== []) {
            $selectedMailshotId = (int) ($mailshots[0]['id'] ?? 0);
        }

        $preview = ['ok' => false, 'rows' => [], 'errors' => ['Create a mailshot before loading recipients.']];
        if ($selectedMailshotId > 0) {
            $preview = $this->runService()->previewRecipients($selectedMailshotId, 100);
        }
        $rows = is_array($preview['rows'] ?? null) ? $preview['rows'] : [];

        $selectedRecipientIndex = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
        if ($selectedRecipientIndex < -1) {
            $selectedRecipientIndex = 0;
        }
        $defaultEmail = $this->runService()->defaultTestEmail()['default_test_email'] ?? '';
        $testEmail = (string) ($this->wp->requestParam('test_email', (string) $defaultEmail) ?? $defaultEmail);
        $result = $this->result();
        if ((string) ($this->wp->requestParam('render_test', '0') ?? '0') === '1') {
            if ($selectedRecipientIndex < 0) {
                $result = ['ok' => false, 'errors' => ['Choose a specific recipient row for Render Test.']];
            } else {
                try {
                    $this->maybeRaiseMemoryLimit($this->resolveDownloadMemoryLimitTarget());
                    $result = $this->runService()->renderTest($selectedMailshotId, $selectedRecipientIndex);
                } catch (\Throwable $e) {
                    $result = ['ok' => false, 'errors' => ['Render test failed: ' . $e->getMessage()]];
                }
            }
            $result['ui_action'] = 'render';
        }

        $action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));

        echo '<div class="wrap feca-mailshots-admin"><h1>Mailshot Test</h1>';
        echo $this->renderAdminUiStyles();
        echo '<p>Render with a selected recipient context, then optionally send one test email.</p>';

        if ($result !== null) {
            $ok = !empty($result['ok']);
            $bannerClass = $ok ? 'feca-banner-success' : 'feca-banner-error';
            echo '<div class="feca-banner ' . $bannerClass . '">';
            echo '<strong>' . ($ok ? 'Test action succeeded.' : 'Test action failed.') . '</strong>';
            if (!empty($result['errors']) && is_array($result['errors'])) {
                echo '<p class="feca-banner-note">' . htmlspecialchars(implode('; ', $result['errors'])) . '</p>';
            }
            if (!empty($result['warnings']) && is_array($result['warnings'])) {
                echo '<p class="feca-banner-note">Warnings: ' . htmlspecialchars(implode('; ', $result['warnings'])) . '</p>';
            }
            if (!empty($result['sent_to'])) {
                echo '<p class="feca-banner-note">Sent to: <code>' . htmlspecialchars((string) $result['sent_to']) . '</code></p>';
            }
            if (!empty($result['sent_at'])) {
                echo '<p class="feca-banner-note">Sent at: <code>' . htmlspecialchars((string) $result['sent_at']) . '</code></p>';
            }
            if (($result['ui_action'] ?? '') === 'render' && !empty($result['ok']) && is_array($result['rendered'] ?? null)) {
                echo '<p class="feca-button-row"><button type="button" class="button button-primary" id="ms-open-render-preview">Open Render Preview</button></p>';
            }
            echo '</div>';
        }

        echo '<div class="feca-panel">';
        echo '<h2 class="feca-section-title">1. Select Mailshot</h2>';
        echo '<form method="get" action="' . htmlspecialchars($this->wp->adminUrl('admin.php')) . '" id="mst-mailshot-select-form">';
        echo '<input type="hidden" name="page" value="feca-mailshots-test">';
        echo '<div class="feca-control-row">';
        echo '<div class="feca-control feca-control-min-360">';
        echo '<label for="mst_mailshot_id"><strong>Mailshot</strong></label>';
        echo '<select id="mst_mailshot_id" name="mailshot_id">';
        foreach ($mailshots as $m) {
            $id = (int) ($m['id'] ?? 0);
            $sel = $id === $selectedMailshotId ? ' selected' : '';
            $label = trim((string) ($m['Purpose'] ?? ''));
            if ($label === '') {
                $label = 'Mailshot #' . $id;
            }
            echo '<option value="' . $id . '"' . $sel . '>' . htmlspecialchars($label) . '</option>';
        }
        echo '</select></div>';
        echo '</div>';
        echo '</form>';
        echo '<script>(function(){var select=document.getElementById("mst_mailshot_id");var form=document.getElementById("mst-mailshot-select-form");if(select&&form){select.addEventListener("change",function(){form.submit();});}})();</script>';
        echo '</div>';

        if (!empty($preview['errors'])) {
            echo '<div class="feca-banner feca-banner-error">' . htmlspecialchars(implode('; ', $preview['errors'])) . '</div>';
        }

        echo '<div id="mst-inline-result"></div>';
        echo '<form method="post" action="' . $action . '" class="feca-form" id="mst-action-form">';
        echo '<h2 class="feca-section-title">2. Render / Send Test</h2>';
        echo $this->hiddenNonceField(self::NONCE_ACTION);
        echo '<input type="hidden" name="mailshot_id" value="' . $selectedMailshotId . '">';

        echo '<div class="feca-control-row">';
        echo '<div class="feca-control feca-control-min-360">';
        echo '<label for="mst_recipient_index"><strong>Recipient row</strong></label>';
        echo '<select id="mst_recipient_index" name="recipient_index" data-recipient-count="' . count($rows) . '">';
        $allSel = $selectedRecipientIndex === -1 ? ' selected' : '';
        echo '<option value="-1"' . $allSel . '>All recipients (send test email to address below for every row)</option>';
        foreach ($rows as $r) {
            $idx = (int) ($r['index'] ?? 0);
            $sel = $idx === $selectedRecipientIndex ? ' selected' : '';
            $email = (string) ($r['recipient_email'] ?? '');
            $key = (string) ($r['recipient_key'] ?? '');
            $text = $email !== '' ? $email : '(no email)';
            if ($key !== '' && strcasecmp($key, $email) !== 0) {
                $text .= ' | ' . $key;
            }
            echo '<option value="' . $idx . '"' . $sel . '>' . htmlspecialchars($text) . '</option>';
        }
        echo '</select></div>';
        echo '<div class="feca-control feca-control-min-420">';
        echo '<label for="mst_test_email"><strong>Test email address</strong></label>';
        echo '<input id="mst_test_email" class="regular-text" type="email" name="test_email" value="' . htmlspecialchars($testEmail, ENT_QUOTES) . '">';
        echo '</div>';
        echo '<div class="feca-control"><label>&nbsp;</label><button class="button" type="submit" name="action" value="feca_mailshots_test_render_ui">Render Test (No Send)</button></div>';
        echo '<div class="feca-control"><label>&nbsp;</label><button class="button button-primary" type="submit" name="action" value="feca_mailshots_test_send_ui" id="mst-send-test-button" onclick="return window.fecaConfirmTestSend ? window.fecaConfirmTestSend() : confirm(\'Send one test email to the entered address?\');">Send Test Email</button></div>';
        echo '</div>';
        echo '</form>';
        echo '<script>(function(){';
        echo 'var apiUrl=' . json_encode($this->wp->adminUrl('admin-post.php?action=feca_mailshots_test_api&op=send_test'), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ';';
        echo 'var form=document.getElementById("mst-action-form");';
        echo 'var inlineResult=document.getElementById("mst-inline-result");';
        echo 'var sendButton=document.getElementById("mst-send-test-button");';
        echo 'function showResult(ok,title,lines){if(!inlineResult){return;}var wrap=document.createElement("div");wrap.className="feca-banner "+(ok?"feca-banner-success":"feca-banner-error");var strong=document.createElement("strong");strong.textContent=title;wrap.appendChild(strong);(lines||[]).forEach(function(line){var p=document.createElement("p");p.className="feca-banner-note";p.textContent=String(line||"");wrap.appendChild(p);});inlineResult.innerHTML="";inlineResult.appendChild(wrap);wrap.scrollIntoView({block:"nearest"});}';
        echo 'if(form){form.addEventListener("submit",function(ev){var submitter=ev.submitter;if(!submitter||String(submitter.value||"")!=="feca_mailshots_test_send_ui"){return;}ev.preventDefault();var payload=new URLSearchParams(new FormData(form));payload.delete("action");if(sendButton){sendButton.disabled=true;}showResult(true,"Sending test email...",["Rendering PDF attachment and sending message."]);fetch(apiUrl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:payload.toString()}).then(function(response){return response.text().then(function(text){var data=null;try{data=JSON.parse(text);}catch(e){return {ok:false,errors:["Send test failed: server returned HTTP "+response.status+" instead of JSON. Check the WordPress/PHP error log for the underlying fatal error."]};}if(!response.ok&&data&&data.ok!==false){data.ok=false;}return data;});}).then(function(data){if(data&&data.ok){var lines=[];if(data.sent_to){lines.push("Sent to: "+data.sent_to);}if(data.sent_at){lines.push("Sent at: "+data.sent_at);}if(data.warnings&&data.warnings.length){lines=lines.concat(data.warnings.map(function(w){return "Warning: "+w;}));}showResult(true,"Test action succeeded.",lines.length?lines:["Sent."]);return;}var errors=(data&&data.errors&&data.errors.length)?data.errors:[(data&&data.error)?data.error:"Unknown send-test failure."];showResult(false,"Test action failed.",errors);}).catch(function(error){showResult(false,"Test action failed.",[error&&error.message?error.message:"Request failed."]);}).finally(function(){if(sendButton){sendButton.disabled=false;}});});}';
        echo 'window.fecaConfirmTestSend=function(){';
        echo 'var select=document.getElementById("mst_recipient_index");';
        echo 'if(!select){return confirm("Send one test email to the entered address?");}';
        echo 'var value=String(select.value||"0");';
        echo 'if(value==="-1"){';
        echo 'var count=parseInt(select.getAttribute("data-recipient-count")||"0",10);';
        echo 'if(!Number.isFinite(count)||count<0){count=0;}';
        echo 'return confirm("Send "+count+" emails to the entered address?");';
        echo '}';
        echo 'return confirm("Send one test email to the entered address?");';
        echo '};';
        echo '})();</script>';

        if ($rows !== []) {
            $sampleColumns = $this->sampleColumns($rows, 4);
            echo '<div class="feca-panel">';
            echo '<h2 class="feca-section-title">Recipient Sample (First 20)</h2>';
            echo '<div class="feca-scroll-frame"><div class="feca-scroll-pane">';
            echo '<table class="widefat striped"><thead><tr><th>#</th><th>Key</th><th>Email</th>';
            foreach ($sampleColumns as $col) {
                echo '<th>' . htmlspecialchars($col) . '</th>';
            }
            echo '</tr></thead><tbody>';
            foreach (array_slice($rows, 0, 20) as $r) {
                $rowData = is_array($r['row'] ?? null) ? $r['row'] : [];
                echo '<tr><td>' . (int) ($r['index'] ?? 0) . '</td><td>' . htmlspecialchars((string) ($r['recipient_key'] ?? '')) . '</td><td>' . htmlspecialchars((string) ($r['recipient_email'] ?? '')) . '</td>';
                foreach ($sampleColumns as $col) {
                    echo '<td>' . htmlspecialchars($this->displayCellValue($rowData[$col] ?? null)) . '</td>';
                }
                echo '</tr>';
            }
            echo '</tbody></table>';
            echo '</div></div>';
            echo '</div>';
        }

        if ($result !== null && ($result['ui_action'] ?? '') === 'render' && !empty($result['ok']) && is_array($result['rendered'] ?? null)) {
            $rendered = $result['rendered'];
            $recipient = $this->selectedRecipientSummary($rows, $selectedRecipientIndex);
            $recipientText = $recipient['email'] ?? '';
            if (($recipient['key'] ?? '') !== '' && strcasecmp((string) ($recipient['key'] ?? ''), (string) ($recipient['email'] ?? '')) !== 0) {
                $recipientText .= ($recipientText !== '' ? ' | ' : '') . (string) $recipient['key'];
            }
            if ($recipientText === '') {
                $recipientText = '(recipient unavailable)';
            }
            $subject = (string) ($rendered['subject'] ?? '');
            $messageHtml = (string) ($rendered['message'] ?? '');
            $pdfHtml = (string) ($rendered['pdf_attachment'] ?? '');

            echo '<div id="ms-render-preview-modal" class="feca-modal-overlay-high">';
            echo '<div class="feca-modal-shell feca-modal-shell-wide">';
            echo '<h2 class="feca-modal-title">Render Preview</h2>';
            echo '<p class="feca-modal-subtitle"><strong>Recipient:</strong> ' . htmlspecialchars($recipientText) . '</p>';
            echo '<p class="feca-modal-subtitle"><strong>Subject:</strong> ' . htmlspecialchars($subject) . '</p>';
            echo '<div class="feca-preview-grid">';
            echo '<div><h3 class="feca-modal-subtitle">Message (HTML)</h3><iframe sandbox="" class="feca-preview-iframe" id="ms-render-message-frame"></iframe></div>';
            echo '<div><h3 class="feca-modal-subtitle">PDF Attachment (HTML)</h3><iframe sandbox="" class="feca-preview-iframe" id="ms-render-pdf-frame"></iframe></div>';
            echo '</div>';
            echo '<p class="feca-button-row"><button type="button" class="button button-primary" id="ms-close-render-preview">Close</button></p>';
            echo '</div></div>';
            echo '<script>(function(){';
            echo 'var messageHtml=' . json_encode($messageHtml, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ';';
            echo 'var pdfHtml=' . json_encode($pdfHtml, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ';';
            echo 'var modal=document.getElementById("ms-render-preview-modal");';
            echo 'var open=document.getElementById("ms-open-render-preview");';
            echo 'var close=document.getElementById("ms-close-render-preview");';
            echo 'var messageFrame=document.getElementById("ms-render-message-frame");';
            echo 'var pdfFrame=document.getElementById("ms-render-pdf-frame");';
            echo 'if(messageFrame){messageFrame.srcdoc=messageHtml;}';
            echo 'if(pdfFrame){pdfFrame.srcdoc=pdfHtml;}';
            echo 'if(!modal){return;}';
            echo 'var show=function(){modal.style.display="block";};';
            echo 'var hide=function(){modal.style.display="none";};';
            echo 'if(open){open.addEventListener("click",show);}';
            echo 'if(close){close.addEventListener("click",hide);}';
            echo 'modal.addEventListener("click",function(ev){if(ev.target===modal){hide();}});';
            echo 'document.addEventListener("keydown",function(ev){if(ev.key==="Escape"&&modal.style.display==="block"){hide();}});';
            echo 'show();';
            echo '})();</script>';
        }

        echo '</div>';
    }

    public function handleApi(): void
    {
        if (!$this->enforceCapabilityOrJson(self::CAPABILITY)) {
            return;
        }

        $op = (string) ($this->wp->requestParam('op', '') ?? '');
        $service = $this->runService();

        try {
            if ($op === 'default_test_email') {
                $this->wp->sendJson(['ok' => true] + $service->defaultTestEmail());
                return;
            }

            if ($op === 'render_test') {
                if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
                    return;
                }
                $this->maybeRaiseMemoryLimit($this->resolveDownloadMemoryLimitTarget());
                $mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
                $idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
                if ($idx < 0) {
                    $this->wp->sendJson(['ok' => false, 'errors' => ['Choose a specific recipient row for Render Test.']]);
                    return;
                }
                $this->wp->sendJson($service->renderTest($mailshotId, $idx));
                return;
            }

            if ($op === 'send_test') {
                if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
                    return;
                }
                $this->maybeRaiseMemoryLimit($this->resolveDownloadMemoryLimitTarget());
                $mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
                $idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
                $to = (string) ($this->wp->requestParam('test_email', '') ?? '');
                if ($idx < 0) {
                    $this->wp->sendJson($service->sendTestAll($mailshotId, $to));
                    return;
                }
                $this->wp->sendJson($service->sendTest($mailshotId, $idx, $to));
                return;
            }

            $this->wp->sendJson(['ok' => false, 'error' => 'Unknown op'], 400);
        } catch (\Throwable $e) {
            $this->wp->sendJson(['ok' => false, 'error' => $e->getMessage()], 500);
        }
    }

    public function handleRenderUi(): void
    {
        if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
            return;
        }
        $mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
        $idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
        if ($idx < 0) {
            $this->redirect($mailshotId, $idx, (string) ($this->wp->requestParam('test_email', '') ?? ''), true);
            return;
        }
        $this->maybeRaiseMemoryLimit($this->resolveDownloadMemoryLimitTarget());
        $this->redirect($mailshotId, $idx, (string) ($this->wp->requestParam('test_email', '') ?? ''), true);
    }

    public function handleSendUi(): void
    {
        if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
            return;
        }
        $mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
        $idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
        $email = (string) ($this->wp->requestParam('test_email', '') ?? '');
        try {
            $this->maybeRaiseMemoryLimit($this->resolveDownloadMemoryLimitTarget());
            if ($idx < 0) {
                $result = $this->runService()->sendTestAll($mailshotId, $email);
            } else {
                $result = $this->runService()->sendTest($mailshotId, $idx, $email);
            }
        } catch (\Throwable $e) {
            $result = ['ok' => false, 'errors' => ['Send test failed: ' . $e->getMessage()]];
        }
        unset($result['rendered']);
        $result['ui_action'] = 'send';
        $this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
        $this->redirect($mailshotId, $idx, $email);
    }

    private function redirect(int $mailshotId, int $recipientIndex, string $testEmail, bool $renderTest = false): void
    {
        $url = $this->wp->adminUrl('admin.php?page=feca-mailshots-test&mailshot_id=' . $mailshotId . '&recipient_index=' . $recipientIndex . '&test_email=' . rawurlencode($testEmail));
        if ($renderTest) {
            $url .= '&render_test=1';
        }
        if (!headers_sent()) {
            header('Location: ' . $url, true, 302);
            exit;
        }
    }

    /** @return array<string,mixed>|null */
    private function result(): ?array
    {
        $raw = $this->wp->getOption(self::RESULT_OPTION_KEY, null);
        $this->wp->deleteOption(self::RESULT_OPTION_KEY);
        return is_array($raw) ? $raw : null;
    }

    private function maybeRaiseMemoryLimit(string $target): void
    {
        if ($target === '' || !function_exists('ini_get') || !function_exists('ini_set')) {
            return;
        }

        $current = (string) ini_get('memory_limit');
        $currentBytes = $this->memoryLimitToBytes($current);
        $targetBytes = $this->memoryLimitToBytes($target);

        if ($currentBytes < 0 || $targetBytes <= 0 || $currentBytes >= $targetBytes) {
            return;
        }

        @ini_set('memory_limit', $target);
    }

    private function resolveDownloadMemoryLimitTarget(): string
    {
        $optionValue = $this->wp->getOption(self::DOWNLOAD_MEMORY_LIMIT_OPTION, '');
        if (is_string($optionValue)) {
            $value = trim($optionValue);
            if ($this->memoryLimitToBytes($value) > 0) {
                return $value;
            }
        }

        $envValue = getenv(self::DOWNLOAD_MEMORY_LIMIT_ENV);
        if (is_string($envValue)) {
            $value = trim($envValue);
            if ($this->memoryLimitToBytes($value) > 0) {
                return $value;
            }
        }

        return '';
    }

    private function memoryLimitToBytes(string $limit): int
    {
        $value = trim($limit);
        if ($value === '') {
            return 0;
        }
        if ($value === '-1') {
            return -1;
        }

        $unit = strtolower(substr($value, -1));
        if (ctype_alpha($unit)) {
            $number = (float) substr($value, 0, -1);
            switch ($unit) {
                case 'g':
                    return (int) ($number * 1024 * 1024 * 1024);
                case 'm':
                    return (int) ($number * 1024 * 1024);
                case 'k':
                    return (int) ($number * 1024);
                default:
                    return (int) $number;
            }
        }

        return (int) $value;
    }

    private function runService(): MailshotRunService
    {
        return ($this->runServiceFactory)();
    }

    private function mailshotService(): MailshotService
    {
        return ($this->mailshotServiceFactory)();
    }

    /**
     * @param list<array<string,mixed>> $rows
     * @return list<string>
     */
    private function sampleColumns(array $rows, int $maxColumns): array
    {
        if ($rows === [] || $maxColumns <= 0) {
            return [];
        }

        $firstRow = is_array($rows[0]['row'] ?? null) ? $rows[0]['row'] : [];
        if ($firstRow === []) {
            return [];
        }
        $firstRecipientKey = trim((string) ($rows[0]['recipient_key'] ?? ''));
        $firstRecipientEmail = trim((string) ($rows[0]['recipient_email'] ?? ''));

        $selected = [];
        $seenValues = [];
        foreach (array_keys($firstRow) as $key) {
            $name = (string) $key;
            if ($name === '') {
                continue;
            }
            if ($this->isIdLikeField($name) || $this->isEmailLikeField($name) || $this->isKeyLikeField($name)) {
                continue;
            }
            $sampleValue = trim($this->displayCellValue($firstRow[$name] ?? null));
            if ($sampleValue !== '') {
                $valueKey = strtolower($sampleValue);
                if ($valueKey === strtolower($firstRecipientKey) || $valueKey === strtolower($firstRecipientEmail)) {
                    continue;
                }
                if (isset($seenValues[$valueKey])) {
                    continue;
                }
                $seenValues[$valueKey] = true;
            }
            $selected[] = $name;
            if (count($selected) >= $maxColumns) {
                break;
            }
        }

        return $selected;
    }

    private function isIdLikeField(string $field): bool
    {
        $name = strtolower($field);
        return $name === 'id'
            || str_ends_with($name, '.id')
            || str_ends_with($name, '_id')
            || str_contains($name, 'accountid');
    }

    private function isEmailLikeField(string $field): bool
    {
        $name = strtolower($field);
        return $name === 'email' || str_ends_with($name, '.email') || str_contains($name, 'email');
    }

    private function isKeyLikeField(string $field): bool
    {
        $name = strtolower($field);
        return $name === 'key'
            || str_ends_with($name, '.key')
            || str_contains($name, 'recipient_key')
            || str_contains($name, 'contact_key');
    }

    /** @param mixed $value */
    private function displayCellValue($value): string
    {
        if ($value === null) {
            return '';
        }
        if (is_scalar($value)) {
            return trim((string) $value);
        }
        return json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '';
    }

    /**
     * @param list<array<string,mixed>> $rows
     * @return array{index:int,key:string,email:string}|array{}
     */
    private function selectedRecipientSummary(array $rows, int $recipientIndex): array
    {
        foreach ($rows as $row) {
            $idx = (int) ($row['index'] ?? -1);
            if ($idx !== $recipientIndex) {
                continue;
            }
            return [
                'index' => $idx,
                'key' => trim((string) ($row['recipient_key'] ?? '')),
                'email' => trim((string) ($row['recipient_email'] ?? '')),
            ];
        }
        return [];
    }

}
