<?php
// Приёмник конверсий из чат-бота (в т.ч. MAX) → Яндекс.Метрика.
//
// Зачем: в боте нет прямой интеграции с Метрикой, зато есть блок
// «Действие → Дополнительно → Запрос во вне», который дергает любой URL.
// На нужном шаге бот вызывает этот скрипт, а тот отправляет цель в Метрику
// через Measurement Protocol (данные появляются в счётчике за ~20 минут).
//
// Пример вызова из бота:
//   GET https://gubtor.ru/mx/cl.php?v={max_start}&g=podpiska_max
//
// Что кладём в {max_start}: ClientID Метрики посетителя (его подставляет скрипт
// на лендинге в ссылку на бота). Форматы:
//   ym1789808879659865165            — только ClientID
//   ym1789808879659865165_1234567890 — ClientID и yclid из объявления
//   1789808879659865165              — просто число
//
// Ответ — JSON (блок «Запрос во вне» требует валидный JSON).
declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');

$MX_DIR = __DIR__;
$LOG = $MX_DIR . '/data/conversions.jsonl';
$ENVF = '/etc/quiz-secrets/metrika.env';

$env = [];
if (is_readable($ENVF)) {
    foreach (file($ENVF, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        if (strpos($line, '=') === false) continue;
        [$k, $v] = explode('=', $line, 2);
        $env[trim($k)] = trim($v);
    }
}
$counter = preg_replace('/\D/', '', $env['METRIKA_COUNTER_ID'] ?? '112573977');
$mpToken = (string)($env['METRIKA_MP_TOKEN'] ?? '');

$raw = trim((string)($_REQUEST['v'] ?? ''));
$goal = (string)($_REQUEST['g'] ?? 'podpiska_max');
$manualCid = trim((string)($_REQUEST['cid'] ?? ''));
$uid = preg_replace('/[^0-9\-]/', '', (string)($_REQUEST['uid'] ?? '')); // id пользователя в боте
$code = preg_replace('/[^A-Za-z0-9]/', '', (string)($_REQUEST['code'] ?? ''));
$bind = !empty($_REQUEST['bind']);
$BIND = $MX_DIR . '/data/bind.jsonl';

// разбираем присланное значение: ClientID можно передать как в start, так и в UTM-метке.
// Поддерживаем составной формат «метка~ym<ClientID>» — он же используется в варианте 3.
$cid = '';
$yclid = '';
$label = '';
$parts = $raw === '' ? [] : explode('~', $raw);
foreach ($parts as $p) {
    $p = trim($p);
    if ($p === '') continue;
    if (preg_match('/ym(\d{6,25})(?:_([A-Za-z0-9\-]{3,64}))?/i', $p, $m)) {
        $cid = $m[1];
        $yclid = $m[2] ?? '';
    } elseif (preg_match('/^\d{6,25}$/', $p)) {
        $cid = $p;
    } elseif ($label === '') {
        $label = mb_substr(preg_replace('/[^\p{L}\p{N} _\-]/u', '', $p), 0, 40);
    }
}
if ($manualCid !== '' && preg_match('/^(\d{6,25})$/', $manualCid, $m)) {
    $cid = $m[1];
}
if ($cid === '' && $raw !== '') {
    if (preg_match('/ym(\d{6,25})(?:_([A-Za-z0-9\-]{3,64}))?/i', $raw, $m)) {
        $cid = $m[1];
        $yclid = $m[2] ?? '';
    } elseif (preg_match('/(\d{15,25})/', $raw, $m)) {
        $cid = $m[1];
    }
}

if (!preg_match('/^[a-z0-9_\-]{2,40}$/i', $goal)) $goal = 'podpiska_max';
if ($yclid === '' && !empty($_REQUEST['yclid'])) $yclid = preg_replace('/[^A-Za-z0-9\-]/', '', (string)$_REQUEST['yclid']);

// связка «пользователь бота ↔ ClientID»: чтобы дальше можно было слать только uid
function readBind(string $path, string $uid): array {
    if ($uid === '' || !is_file($path)) return [];
    $out = [];
    foreach (array_slice(file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES), -800) as $l) {
        $r = json_decode($l, true);
        if (is_array($r) && (string)($r['uid'] ?? '') === $uid && !empty($r['cid'])) $out = $r;
    }
    return $out;
}

if ($cid !== '' && $uid !== '' && ($bind || $code !== '')) {
    if (!is_dir(dirname($BIND))) @mkdir(dirname($BIND), 0775, true);
    @file_put_contents($BIND, json_encode(['ts' => time(), 'uid' => $uid, 'code' => $code, 'cid' => $cid, 'yclid' => $yclid], JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND | LOCK_EX);
}
if ($cid === '' && $uid !== '') {
    $b = readBind($BIND, $uid);
    if (!empty($b['cid'])) { $cid = (string)$b['cid']; $yclid = (string)($b['yclid'] ?? ''); }
}

$out = ['ok' => false, 'cid' => $cid, 'yclid' => $yclid, 'label' => $label, 'goal' => $goal, 'uid' => $uid, 'error' => ''];

if ($cid === '') {
    $out['error'] = 'не найден ClientID: передайте v={max_start} (или utm-метку), либо uid уже известного пользователя';
    echo json_encode($out, JSON_UNESCAPED_UNICODE);
    exit;
}
if ($mpToken === '') {
    $out['error'] = 'Measurement Protocol не настроен (нет токена в metrika.env)';
    echo json_encode($out, JSON_UNESCAPED_UNICODE);
    exit;
}

// защита от двойного счёта: та же цель для того же посетителя — один раз в 20 часов
$dup = false;
if (is_file($LOG)) {
    $needle = '"cid":"' . $cid . '"';
    $lines = array_slice(file($LOG, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES), -400);
    foreach ($lines as $l) {
        if (strpos($l, $needle) === false) continue;
        $r = json_decode($l, true);
        if (!is_array($r)) continue;
        if (($r['goal'] ?? '') === $goal && (time() - (int)($r['ts'] ?? 0)) < 72000) { $dup = true; break; }
    }
}

$sent = false;
$answer = '';
if (!$dup) {
    $params = http_build_query([
        'tid' => $counter,
        'cid' => $cid,
        't' => 'event',
        'ea' => $goal,
        'ms' => $mpToken,
        'et' => time(),
    ]);
    $ctx = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: " . strlen($params) . "\r\n",
            'content' => $params,
            'timeout' => 15,
            'ignore_errors' => true,
        ],
    ]);
    $answer = (string)@file_get_contents('https://mc.yandex.ru/collect', false, $ctx);
    $sent = strpos($answer, 'OK') !== false;

    if (!is_dir(dirname($LOG))) @mkdir(dirname($LOG), 0775, true);
    @file_put_contents($LOG, json_encode([
        'ts' => time(),
        'cid' => $cid,
        'yclid' => $yclid,
        'label' => $label,
        'uid' => $uid,
        'goal' => $goal,
        'sent' => $sent,
        'src' => 'bot',
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
    ], JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND | LOCK_EX);
}

$out['ok'] = $dup || $sent;
$out['sent'] = $sent;
$out['dedup'] = $dup;
if (!$out['ok']) $out['error'] = 'Метрика не приняла событие: ' . trim(mb_substr(strip_tags($answer), 0, 120));
echo json_encode($out, JSON_UNESCAPED_UNICODE);
