#!/usr/bin/env php
<?php
/**
 * 广告来源限制 Guard 单元验证
 *
 * 用法：php tools/verify_ad_source_guard.php
 */
if (PHP_SAPI !== 'cli') {
    fwrite(STDERR, "请在 CLI 下运行\n");
    exit(1);
}

require_once dirname(__DIR__) . '/lib/CloakAdSourceGuard.php';

$passed = 0;
$failed = 0;

function vassert($name, $cond, $detail = '')
{
    global $passed, $failed;
    if ($cond) {
        $passed++;
        return;
    }
    $failed++;
    echo "FAIL: {$name}\n";
    if ($detail !== '') {
        echo "  {$detail}\n";
    }
}

$spmId = 'Ab12Cd34Ef56';
$baseCfg = [
    'ad_fb'     => true,
    'ad_google' => false,
    'ad_tiktok' => false,
    'ad_strict' => false,
    'ad_spm_id' => $spmId,
];

// 优先级 1：ad_spm_id 匹配
$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => ['ad_spm_id' => $spmId], 'user_agent' => '',
]);
vassert('spm_id 匹配放行', $r['passed'] && $r['matched_by'] === 'ad_spm_id');

$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => ['ad_spm_id' => 'wrongId12345'], 'user_agent' => '',
]);
vassert('spm_id 不匹配不单独放行', !$r['passed'] || $r['matched_by'] !== 'ad_spm_id');

// 优先级 2：clid
$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => ['fbclid' => 'IwAR1234567890123456789012'], 'user_agent' => '',
]);
vassert('fbclid 放行', $r['passed'] && strpos($r['matched_by'], 'clid:') === 0);

$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => ['utm_campaign' => '__CAMPAIGN_ID__'], 'user_agent' => '',
]);
vassert('TikTok 宏放行', $r['passed'] && strpos($r['matched_by'], 'tiktok_macro:') === 0);

// 优先级 3：referer
$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => 'https://www.facebook.com/ads', 'query' => [], 'user_agent' => '',
]);
vassert('referer facebook 放行', $r['passed'] && strpos($r['matched_by'], 'referer:') === 0);

// 优先级 4：UA
$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 TikTok 26.0',
]);
vassert('UA TikTok 放行', $r['passed'] && strpos($r['matched_by'], 'ua:') === 0);

// 严格模式拦截
$strictCfg = array_merge($baseCfg, ['ad_strict' => true]);
$r = CloakAdSourceGuard::evaluate($strictCfg, [
    'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
]);
vassert('严格模式未命中拦截', !$r['passed'] && $r['reason'] === '来源限制:未命中广告来源验证');

// 非严格模式
$r = CloakAdSourceGuard::evaluate($baseCfg, [
    'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
]);
vassert('非严格未命中', !$r['passed'] && $r['reason'] === '未确定流量来源');

// guard 未启用
$r = CloakAdSourceGuard::evaluate([
    'ad_fb' => false, 'ad_google' => false, 'ad_tiktok' => false,
    'ad_strict' => true, 'ad_spm_id' => $spmId,
], ['referer' => '', 'query' => [], 'user_agent' => '']);
vassert('无平台开启跳过', $r['passed'] && $r['matched_by'] === 'guard_disabled');

// 链接拼装：单平台
$q = CloakAdSourceGuard::buildAdLinkQuery([CloakAdSourceGuard::PLATFORM_FB], $spmId);
vassert('单平台 FB 含完整 UTM', strpos($q, 'utm_source=facebook') !== false && strpos($q, 'campaign_id={{campaign.id}}') !== false);
vassert('单平台含 ad_spm_id', strpos($q, 'ad_spm_id=' . rawurlencode($spmId)) !== false);

// 链接拼装：多平台
$q = CloakAdSourceGuard::buildAdLinkQuery([
    CloakAdSourceGuard::PLATFORM_FB,
    CloakAdSourceGuard::PLATFORM_GOOGLE,
    CloakAdSourceGuard::PLATFORM_TIKTOK,
], $spmId);
vassert('多平台含 FB 代表参数', strpos($q, 'campaign_id={{campaign.id}}') !== false);
vassert('多平台含 Google 代表参数', strpos($q, 'utm_campaign={campaignid}') !== false);
vassert('多平台含 TikTok 代表参数', strpos($q, 'adgroup_id=__CID__') !== false);
vassert('多平台不含完整 FB UTM', strpos($q, 'utm_medium=paid_social') === false);

// spm 生成
$id = CloakAdSourceGuard::generateSpmId();
vassert('generateSpmId 合法', CloakAdSourceGuard::isValidSpmId($id), $id);

// 临时链接计时触发
vassert('计时：referer 命中广告来源', CloakAdSourceGuard::shouldStartUrlArgsTimer($baseCfg, [
    'referer' => 'https://www.facebook.com/', 'query' => [], 'user_agent' => '',
]));
vassert('计时：未命中不启动', !CloakAdSourceGuard::shouldStartUrlArgsTimer($baseCfg, [
    'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
]));
vassert('计时：guard 关闭时 clid 仍可触发', CloakAdSourceGuard::shouldStartUrlArgsTimer([
    'ad_fb' => false, 'ad_google' => false, 'ad_tiktok' => false,
    'ad_strict' => false, 'ad_spm_id' => '',
], ['referer' => '', 'query' => ['gclid' => 'abc'], 'user_agent' => '']));

$result = ['ok' => $failed === 0, 'passed' => $passed, 'failed' => $failed];
echo json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
exit($failed > 0 ? 1 : 0);
