<?php

declare(strict_types=1);

namespace app\common\service;

use think\Log;

/**
 * 分流中转页服务端像素回传（Facebook CAPI / TikTok Events API）
 */
class SplitPixelPostbackService
{
    /**
     * @param array{facebook: array<int, array<string, mixed>>, tiktok: array<int, array<string, mixed>>} $config
     */
    public static function dispatch(array $config, string $clientIp, string $userAgent, string $eventSourceUrl = ''): void
    {
        foreach (SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_FACEBOOK) as $row) {
            if ((int) ($row['server_postback'] ?? 0) !== 1) {
                continue;
            }
            $token = trim((string) ($row['access_token'] ?? ''));
            if ($token === '') {
                continue;
            }
            self::sendFacebook($row, $token, $clientIp, $userAgent, $eventSourceUrl);
        }

        foreach (SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_TIKTOK) as $row) {
            if ((int) ($row['server_postback'] ?? 0) !== 1) {
                continue;
            }
            $token = trim((string) ($row['access_token'] ?? ''));
            if ($token === '') {
                continue;
            }
            self::sendTikTok($row, $token, $clientIp, $userAgent, $eventSourceUrl);
        }
    }

    /**
     * @param array<string, mixed> $row
     */
    private static function sendFacebook(array $row, string $accessToken, string $clientIp, string $userAgent, string $eventSourceUrl): void
    {
        $pixelId = trim((string) ($row['pixel_id'] ?? ''));
        if ($pixelId === '') {
            return;
        }

        $eventName = (string) ($row['event'] ?? 'PageView');
        $testCode = trim((string) ($row['test_code'] ?? ''));

        $payload = [
            'data' => [[
                'event_name'    => $eventName,
                'event_time'    => time(),
                'action_source' => 'website',
                'user_data'     => array_filter([
                    'client_ip_address' => $clientIp !== '' ? $clientIp : null,
                    'client_user_agent' => $userAgent !== '' ? $userAgent : null,
                ]),
            ]],
        ];
        if ($eventSourceUrl !== '') {
            $payload['data'][0]['event_source_url'] = $eventSourceUrl;
        }
        if ($testCode !== '') {
            $payload['test_event_code'] = $testCode;
        }

        $url = 'https://graph.facebook.com/v19.0/' . rawurlencode($pixelId) . '/events?access_token=' . rawurlencode($accessToken);
        self::postJson($url, $payload, [], 'facebook', $pixelId);
    }

    /**
     * @param array<string, mixed> $row
     */
    private static function sendTikTok(array $row, string $accessToken, string $clientIp, string $userAgent, string $eventSourceUrl): void
    {
        $pixelId = trim((string) ($row['pixel_id'] ?? ''));
        if ($pixelId === '') {
            return;
        }

        $event = self::mapTikTokServerEvent((string) ($row['event'] ?? 'PageView'));
        $testCode = trim((string) ($row['test_code'] ?? ''));

        $payload = [
            'pixel_code' => $pixelId,
            'event'      => $event,
            'event_id'   => uniqid('split_', true),
            'timestamp'  => gmdate('c'),
            'context'    => array_filter([
                'ip'         => $clientIp !== '' ? $clientIp : null,
                'user_agent' => $userAgent !== '' ? $userAgent : null,
                'page'       => $eventSourceUrl !== '' ? ['url' => $eventSourceUrl] : null,
            ]),
        ];
        if ($testCode !== '') {
            $payload['test_event_code'] = $testCode;
        }

        $url = 'https://business-api.tiktok.com/open_api/v1.3/event/track/';
        self::postJson($url, $payload, [
            'Access-Token: ' . $accessToken,
            'Content-Type: application/json',
        ], 'tiktok', $pixelId);
    }

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

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

    /**
     * @param array<string, mixed> $payload
     * @param array<int, string>   $headers
     */
    private static function postJson(string $url, array $payload, array $headers, string $platform, string $pixelId): void
    {
        $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json === false) {
            return;
        }

        $ch = curl_init($url);
        if ($ch === false) {
            return;
        }

        $defaultHeaders = ['Content-Type: application/json'];
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $json,
            CURLOPT_HTTPHEADER     => array_merge($defaultHeaders, $headers),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 3,
            CURLOPT_CONNECTTIMEOUT => 2,
        ]);

        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($errno !== 0 || ($httpCode >= 400 && $httpCode !== 0)) {
            Log::error(sprintf(
                'Split pixel postback failed platform=%s pixel=%s http=%d curl=%d',
                $platform,
                $pixelId,
                $httpCode,
                $errno
            ));
        } elseif (is_string($response) && strpos($response, '"error"') !== false) {
            Log::error(sprintf('Split pixel postback api error platform=%s pixel=%s', $platform, $pixelId));
        }
    }
}
