<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\EmailMarketing;

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

final class MessageCompiler
{
    public function __construct(private TokenService $tokens)
    {
    }

    /**
     * @param array<string,mixed> $campaign
     * @param array<string,mixed> $subscriber
     * @param array<string,mixed> $recipient
     * @param array<string,mixed> $settings
     * @return array{subject:string,html:string,text:string,headers:array<int,string>,message_id:string}
     */
    public function campaign(array $campaign, array $subscriber, array $recipient, array $settings): array
    {
        $campaignId = (int) ($campaign['id'] ?? 0);
        $recipientId = (int) ($recipient['id'] ?? 0);
        $subscriberId = (int) ($subscriber['id'] ?? 0);
        $tracking = ! empty($campaign['tracking_enabled']) && ! empty($settings['tracking_enabled']);

        return $this->compile(
            (string) ($campaign['subject'] ?? ''),
            (string) ($campaign['preview_text'] ?? ''),
            (string) ($campaign['content_html'] ?? ''),
            (string) ($campaign['content_text'] ?? ''),
            $subscriber,
            $settings,
            [
                'tracking' => $tracking,
                'open_url' => $this->campaignOpenUrl($campaignId, $recipientId, $subscriberId),
                'click_url' => function (string $destination) use ($campaignId, $recipientId, $subscriberId): string {
                    return $this->campaignClickUrl($campaignId, $recipientId, $subscriberId, $destination);
                },
                'message_key' => 'campaign-' . $campaignId . '-' . $recipientId,
                'campaign_id' => $campaignId,
                'recipient_id' => $recipientId,
            ]
        );
    }

    /**
     * @param array<string,mixed> $automation
     * @param array<string,mixed> $step
     * @param array<string,mixed> $enrollment
     * @param array<string,mixed> $subscriber
     * @param array<string,mixed> $settings
     * @return array{subject:string,html:string,text:string,headers:array<int,string>,message_id:string}
     */
    public function automation(array $automation, array $step, array $enrollment, array $subscriber, array $settings): array
    {
        $automationId = (int) ($automation['id'] ?? 0);
        $enrollmentId = (int) ($enrollment['id'] ?? 0);
        $stepId = (int) ($step['id'] ?? 0);
        $subscriberId = (int) ($subscriber['id'] ?? 0);
        $tracking = ! empty($settings['tracking_enabled']);

        return $this->compile(
            (string) ($step['subject'] ?? ''),
            '',
            (string) ($step['content_html'] ?? ''),
            (string) ($step['content_text'] ?? ''),
            $subscriber,
            $settings,
            [
                'tracking' => $tracking,
                'open_url' => $this->automationOpenUrl($automationId, $enrollmentId, $stepId, $subscriberId),
                'click_url' => function (string $destination) use ($automationId, $enrollmentId, $stepId, $subscriberId): string {
                    return $this->automationClickUrl($automationId, $enrollmentId, $stepId, $subscriberId, $destination);
                },
                'message_key' => 'automation-' . $automationId . '-' . $enrollmentId . '-' . $stepId,
                'automation_id' => $automationId,
                'enrollment_id' => $enrollmentId,
                'step_id' => $stepId,
            ]
        );
    }

    /**
     * @param array<string,mixed> $subscriber
     * @param array<string,mixed> $settings
     * @param array<string,mixed> $context
     * @return array{subject:string,html:string,text:string,headers:array<int,string>,message_id:string}
     */
    private function compile(
        string $subject,
        string $previewText,
        string $contentHtml,
        string $contentText,
        array $subscriber,
        array $settings,
        array $context
    ): array {
        $unsubscribeUrl = $this->unsubscribeUrl($subscriber, $context);
        $textMerge = $this->mergeValues($subscriber, $unsubscribeUrl, false);
        $htmlMerge = $this->mergeValues($subscriber, $unsubscribeUrl, true);
        $subject = sanitize_text_field(strtr($subject, $textMerge));
        $previewText = sanitize_text_field(strtr($previewText, $textMerge));
        $contentHtml = wp_kses_post(strtr($contentHtml, $htmlMerge));
        $contentText = sanitize_textarea_field(strtr($contentText, $textMerge));

        if (! empty($context['tracking']) && isset($context['click_url']) && is_callable($context['click_url'])) {
            $contentHtml = $this->rewriteLinks($contentHtml, $context['click_url']);
        }

        $brand = sanitize_text_field((string) ($settings['from_name'] ?? get_bloginfo('name')));
        $postal = sanitize_textarea_field((string) ($settings['postal_address'] ?? ''));
        $privacy = esc_url_raw((string) ($settings['privacy_url'] ?? ''));
        $preheader = $previewText !== ''
            ? '<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent;">' . esc_html($previewText) . '</div>'
            : '';
        $footerParts = [];
        if ($postal !== '') {
            $footerParts[] = nl2br(esc_html($postal));
        }
        if ($privacy !== '') {
            $footerParts[] = '<a href="' . esc_url($privacy) . '" style="color:#315f8c;">' . esc_html__('Privacy', 'help4-builder-suite') . '</a>';
        }
        $footerParts[] = '<a href="' . esc_url($unsubscribeUrl) . '" style="color:#315f8c;">' . esc_html__('Unsubscribe', 'help4-builder-suite') . '</a>';

        $pixel = '';
        if (! empty($context['tracking']) && ! empty($context['open_url'])) {
            $pixel = '<img src="' . esc_url((string) $context['open_url']) . '" width="1" height="1" alt="" style="display:block;width:1px;height:1px;border:0;" aria-hidden="true">';
        }

        $html = '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>'
            . '<body style="margin:0;padding:0;background:#f4f7fb;color:#172033;font-family:Arial,Helvetica,sans-serif;">'
            . $preheader
            . '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="width:100%;background:#f4f7fb;"><tr><td align="center" style="padding:24px 12px;">'
            . '<table role="presentation" width="640" cellspacing="0" cellpadding="0" style="width:100%;max-width:640px;background:#ffffff;border-collapse:collapse;">'
            . '<tr><td style="padding:20px 24px;background:#071a33;color:#ffffff;font-size:18px;font-weight:700;">' . esc_html($brand) . '</td></tr>'
            . '<tr><td style="padding:28px 24px;font-size:16px;line-height:1.6;">' . $contentHtml . '</td></tr>'
            . '<tr><td style="padding:20px 24px;background:#eef4fa;color:#40516a;font-size:12px;line-height:1.6;text-align:center;">' . implode(' &nbsp;|&nbsp; ', $footerParts) . '</td></tr>'
            . '</table>' . $pixel . '</td></tr></table></body></html>';

        if ($contentText === '') {
            $contentText = trim(wp_strip_all_tags(str_replace(['<br>', '<br/>', '<br />', '</p>', '</li>'], ["\n", "\n", "\n", "\n\n", "\n"], $contentHtml)));
        }
        $textFooter = "\n\n" . $brand;
        if ($postal !== '') {
            $textFooter .= "\n" . $postal;
        }
        if ($privacy !== '') {
            $textFooter .= "\n" . __('Privacy:', 'help4-builder-suite') . ' ' . $privacy;
        }
        $textFooter .= "\n" . __('Unsubscribe:', 'help4-builder-suite') . ' ' . $unsubscribeUrl;

        $messageKey = sanitize_key((string) ($context['message_key'] ?? 'message'));
        $host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $host = $host !== '' ? strtolower($host) : 'localhost';
        $messageId = sprintf('<h4bs-%s-%s@%s>', $messageKey, wp_generate_uuid4(), $host);
        $fromName = $this->headerValue((string) ($settings['from_name'] ?? get_bloginfo('name')));
        $fromEmail = sanitize_email((string) ($settings['from_email'] ?? get_option('admin_email')));
        $replyTo = sanitize_email((string) ($settings['reply_to'] ?? $fromEmail));
        $headers = [
            'Content-Type: text/html; charset=UTF-8',
            'From: ' . $fromName . ' <' . $fromEmail . '>',
            'Reply-To: ' . $replyTo,
            'List-Unsubscribe: <' . $unsubscribeUrl . '>',
            'List-Unsubscribe-Post: List-Unsubscribe=One-Click',
            'X-Help4-Message-ID: ' . $messageId,
        ];

        return [
            'subject' => $subject,
            'html' => $html,
            'text' => trim($contentText . $textFooter),
            'headers' => $headers,
            'message_id' => $messageId,
        ];
    }

    /**
     * @param array<string,mixed> $subscriber
     * @return array<string,string>
     */
    private function mergeValues(array $subscriber, string $unsubscribeUrl, bool $forHtml): array
    {
        $values = [
            '{{first_name}}' => sanitize_text_field((string) ($subscriber['first_name'] ?? '')),
            '{{last_name}}' => sanitize_text_field((string) ($subscriber['last_name'] ?? '')),
            '{{email}}' => sanitize_email((string) ($subscriber['email'] ?? '')),
            '{{site_name}}' => sanitize_text_field((string) get_bloginfo('name')),
            '{{unsubscribe_url}}' => $unsubscribeUrl,
        ];
        if ($forHtml) {
            foreach ($values as $key => $value) {
                $values[$key] = $key === '{{unsubscribe_url}}' ? esc_url($value) : esc_html($value);
            }
        }
        return $values;
    }

    private function rewriteLinks(string $html, callable $linkBuilder): string
    {
        if (! class_exists('WP_HTML_Tag_Processor')) {
            return $html;
        }

        $processor = new \WP_HTML_Tag_Processor($html);
        while ($processor->next_tag(['tag_name' => 'A'])) {
            $href = (string) $processor->get_attribute('href');
            if (! $this->isTrackableUrl($href)) {
                continue;
            }
            $processor->set_attribute('href', (string) $linkBuilder($href));
        }
        return $processor->get_updated_html();
    }

    private function isTrackableUrl(string $url): bool
    {
        $scheme = strtolower((string) wp_parse_url($url, PHP_URL_SCHEME));
        return in_array($scheme, ['http', 'https'], true);
    }

    /**
     * @param array<string,mixed> $subscriber
     * @param array<string,mixed> $context
     */
    private function unsubscribeUrl(array $subscriber, array $context): string
    {
        $subscriberId = (int) ($subscriber['id'] ?? 0);
        $email = sanitize_email((string) ($subscriber['email'] ?? ''));
        $arguments = [
            'action' => 'h4bs_email_unsubscribe',
            's' => $subscriberId,
            't' => $this->tokens->sign('unsubscribe', [$subscriberId, strtolower($email)]),
        ];
        if (! empty($context['campaign_id']) && ! empty($context['recipient_id'])) {
            $arguments['c'] = (int) $context['campaign_id'];
            $arguments['r'] = (int) $context['recipient_id'];
        }
        return add_query_arg($arguments, admin_url('admin-post.php'));
    }

    private function campaignOpenUrl(int $campaignId, int $recipientId, int $subscriberId): string
    {
        return add_query_arg([
            'action' => 'h4bs_email_open',
            'kind' => 'campaign',
            'c' => $campaignId,
            'r' => $recipientId,
            's' => $subscriberId,
            't' => $this->tokens->sign('campaign-open', [$campaignId, $recipientId, $subscriberId]),
        ], admin_url('admin-post.php'));
    }

    private function campaignClickUrl(int $campaignId, int $recipientId, int $subscriberId, string $destination): string
    {
        $encoded = rtrim(strtr(base64_encode($destination), '+/', '-_'), '=');
        return add_query_arg([
            'action' => 'h4bs_email_click',
            'kind' => 'campaign',
            'c' => $campaignId,
            'r' => $recipientId,
            's' => $subscriberId,
            'u' => $encoded,
            't' => $this->tokens->sign('campaign-click', [$campaignId, $recipientId, $subscriberId, $destination]),
        ], admin_url('admin-post.php'));
    }

    private function automationOpenUrl(int $automationId, int $enrollmentId, int $stepId, int $subscriberId): string
    {
        return add_query_arg([
            'action' => 'h4bs_email_open',
            'kind' => 'automation',
            'a' => $automationId,
            'e' => $enrollmentId,
            'step' => $stepId,
            's' => $subscriberId,
            't' => $this->tokens->sign('automation-open', [$automationId, $enrollmentId, $stepId, $subscriberId]),
        ], admin_url('admin-post.php'));
    }

    private function automationClickUrl(int $automationId, int $enrollmentId, int $stepId, int $subscriberId, string $destination): string
    {
        $encoded = rtrim(strtr(base64_encode($destination), '+/', '-_'), '=');
        return add_query_arg([
            'action' => 'h4bs_email_click',
            'kind' => 'automation',
            'a' => $automationId,
            'e' => $enrollmentId,
            'step' => $stepId,
            's' => $subscriberId,
            'u' => $encoded,
            't' => $this->tokens->sign('automation-click', [$automationId, $enrollmentId, $stepId, $subscriberId, $destination]),
        ], admin_url('admin-post.php'));
    }

    private function headerValue(string $value): string
    {
        return trim((string) preg_replace('/[\r\n]+/', ' ', sanitize_text_field($value)));
    }
}
