<?php

declare(strict_types=1);

namespace app\common\service;

/**
 * 中转页浏览器 Pixel 脚本渲染（Facebook / TikTok）
 */
class SplitPixelBrowserRenderer
{
    /**
     * @param array{facebook: array<int, array<string, mixed>>, tiktok: array<int, array<string, mixed>>} $config
     * @return array{
     *   head_html: string,
     *   body_html: string,
     *   track_lines: array<int, string>,
     *   track_jobs: array<int, array<string, mixed>>
     * }
     */
    public static function render(array $config): array
    {
        $fbRows = SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_FACEBOOK);
        $tkRows = SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_TIKTOK);

        $head = [];
        $initLines = [];
        $trackLines = [];
        $jobs = [];

        if ($fbRows !== []) {
            $head[] = self::facebookLoaderScript();
        }
        if ($tkRows !== []) {
            $head[] = self::tiktokLoaderScript();
        }

        foreach ($fbRows as $row) {
            $pixelId = self::escapeJsString((string) ($row['pixel_id'] ?? ''));
            $testCode = trim((string) ($row['test_code'] ?? ''));
            $event = self::escapeJsString((string) ($row['event'] ?? 'PageView'));
            if ($pixelId === '') {
                continue;
            }
            if ($testCode !== '') {
                $testEsc = self::escapeJsString($testCode);
                $initLines[] = "if(typeof fbq!=='undefined'){fbq('init','{$pixelId}',{},{test_event_code:'{$testEsc}'});}";
            } else {
                $initLines[] = "if(typeof fbq!=='undefined'){fbq('init','{$pixelId}');}";
            }
            $jobs[] = [
                'platform' => 'facebook',
                'event'    => (string) ($row['event'] ?? 'PageView'),
                'pixel_id' => (string) ($row['pixel_id'] ?? ''),
            ];
            $trackLines[] = "if(typeof fbq!=='undefined'){fbq('track','{$event}');}";
        }

        foreach ($tkRows as $row) {
            $pixelId = self::escapeJsString((string) ($row['pixel_id'] ?? ''));
            $event = self::mapTikTokBrowserEvent((string) ($row['event'] ?? 'PageView'));
            $eventJs = self::escapeJsString($event);
            if ($pixelId === '') {
                continue;
            }
            $initLines[] = "if(typeof ttq!=='undefined'){ttq.load('{$pixelId}');ttq.page();}";
            $jobs[] = [
                'platform' => 'tiktok',
                'event'    => $event,
                'pixel_id' => (string) ($row['pixel_id'] ?? ''),
            ];
            $trackLines[] = "if(typeof ttq!=='undefined'){ttq.track('{$eventJs}');}";
        }

        return [
            'head_html'   => implode("\n", $head),
            'body_html'   => self::wrapScriptBlock($initLines),
            'track_lines' => $trackLines,
            'track_jobs'  => $jobs,
        ];
    }

    /**
     * 跳转 orchestrator：在 setTimeout 回调内触发像素事件后再 replace
     *
     * @param array<int, string> $trackLines
     */
    public static function renderRedirectOrchestrator(string $redirectUrlJson, array $trackLines = [], int $maxWaitMs = 1500): string
    {
        $trackBlock = $trackLines !== [] ? implode("\n        ", $trackLines) : '';
        $script = self::renderRedirectOrchestratorScript();

        return str_replace(
            ['{$redirectUrlJson}', '{$maxWaitMs}', '{$trackBlock}'],
            [$redirectUrlJson, (string) $maxWaitMs, $trackBlock],
            $script
        );
    }

    private static function renderRedirectOrchestratorScript(): string
    {
        return <<<'JS'
<script type="text/javascript">
(function () {
    var url = {$redirectUrlJson};
    if (!url) {
        return;
    }
    setTimeout(function () {
        {$trackBlock}
        window.location.replace(url);
    }, {$maxWaitMs});
})();
</script>
JS;
    }

    /**
     * @param array<int, string> $lines
     */
    private static function wrapScriptBlock(array $lines): string
    {
        if ($lines === []) {
            return '';
        }

        return '<script type="text/javascript">' . "\n"
            . implode("\n", $lines) . "\n"
            . '</script>';
    }

    private static function facebookLoaderScript(): string
    {
        return <<<'HTML'
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');
</script>
HTML;
    }

    private static function tiktokLoaderScript(): string
    {
        return <<<'HTML'
<script>
!function(w,d,t){w.TiktokAnalyticsObject=t;var ttq=w[t]=w[t]||[];ttq.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie"],ttq.setAndDefer=function(t,e){t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}};for(var i=0;i<ttq.methods.length;i++)ttq.setAndDefer(ttq,ttq.methods[i]);ttq.instance=function(t){for(var e=ttq._i[t]||[],n=0;n<ttq.methods.length;n++)ttq.setAndDefer(e,ttq.methods[n]);return e},ttq.load=function(e,n){var i="https://analytics.tiktok.com/i18n/pixel/events.js";ttq._i=ttq._i||{},ttq._i[e]=[],ttq._i[e]._u=i,ttq._t=ttq._t||{},ttq._t[e]=+new Date,ttq._o=ttq._o||{},ttq._o[e]=n||{};var o=document.createElement("script");o.type="text/javascript",o.async=!0,o.src=i+"?sdkid="+e+"&lib="+t;var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(o,a)}}(window,document,'ttq');
</script>
HTML;
    }

    private static function mapTikTokBrowserEvent(string $event): string
    {
        $map = [
            'PageView'   => 'Pageview',
            'Lead'       => 'SubmitForm',
            'Contact'    => 'Contact',
            'AddToCart'  => 'AddToCart',
            'Purchase'   => 'CompletePayment',
            'Subscribe'  => 'Subscribe',
        ];

        return $map[$event] ?? 'Pageview';
    }

    private static function escapeJsString(string $value): string
    {
        return str_replace(['\\', "'"], ['\\\\', "\\'"], $value);
    }
}
