<?php
declare(strict_types=1);

if (PHP_SAPI !== 'cli') { http_response_code(403); exit('CLI only'); }

/**
 * READ-ONLY проверка: у каких пользователей Битрикс24 их email = живой ящик в Exchange.
 * Ключ матчинга один — email. Проверка фактом: делегатный GetFolder calendar по адресу.
 *   NoError               -> ящик есть  => matched (кандидат в sync_users)
 *   ErrorNonExistentMailbox / прочее -> ящика нет / недоступен => no-mailbox
 * Ничего не пишет. Итог + CSV.
 *
 * Запуск:  php bin/match.php [csv=match_report.csv]
 */

require __DIR__ . '/../vendor/autoload.php';
$cfg = require __DIR__ . '/../config.php';

use App\BitrixClient;

$csvPath = $argv[1] ?? (__DIR__ . '/../match_report.csv');

const NS_S = 'http://schemas.xmlsoap.org/soap/envelope/';
const NS_T = 'http://schemas.microsoft.com/exchange/services/2006/types';
const NS_M = 'http://schemas.microsoft.com/exchange/services/2006/messages';

$ex       = $cfg['exchange'];
$endpoint = 'https://' . $ex['server'] . '/EWS/Exchange.asmx';
$version  = $ex['version'] ?: 'Exchange2013';

function esc(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_XML1, 'UTF-8'); }

/**
 * Проверка одного ящика: делегатный GetFolder calendar.
 * @return array{ok:bool, code:string}  ok=true при NoError; иначе code = ResponseCode/фраза.
 */
function mailboxProbe(string $endpoint, string $user, string $pass, string $version, string $smtp): array
{
    $env = '<?xml version="1.0" encoding="utf-8"?>'
        . '<soap:Envelope xmlns:soap="' . NS_S . '" xmlns:t="' . NS_T . '" xmlns:m="' . NS_M . '">'
        . '<soap:Header><t:RequestServerVersion Version="' . esc($version) . '"/></soap:Header>'
        . '<soap:Body><m:GetFolder>'
        . '<m:FolderShape><t:BaseShape>IdOnly</t:BaseShape></m:FolderShape>'
        . '<m:FolderIds><t:DistinguishedFolderId Id="calendar">'
        . '<t:Mailbox><t:EmailAddress>' . esc($smtp) . '</t:EmailAddress></t:Mailbox>'
        . '</t:DistinguishedFolderId></m:FolderIds>'
        . '</m:GetFolder></soap:Body></soap:Envelope>';

    $ch = curl_init($endpoint);
    curl_setopt_array($ch, [
        CURLOPT_POST => true, CURLOPT_POSTFIELDS => $env,
        CURLOPT_HTTPHEADER => ['Content-Type: text/xml; charset=utf-8'],
        CURLOPT_USERPWD => "$user:$pass", CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
    ]);
    $raw = curl_exec($ch);
    $err = curl_error($ch);
    curl_close($ch);
    if ($raw === false) { return ['ok' => false, 'code' => 'transport: ' . $err]; }

    $dom = new DOMDocument();
    if (!@$dom->loadXML((string)$raw)) { return ['ok' => false, 'code' => 'non-xml']; }
    $xp = new DOMXPath($dom);
    $xp->registerNamespace('m', NS_M);

    // ResponseCode из сообщения или из fault (local-name, чтобы не зависеть от префикса).
    $rc = '';
    foreach ($xp->query('//m:ResponseCode') as $n) { $rc = trim($n->nodeValue); break; }
    if ($rc === '') {
        foreach ($xp->query('//*[local-name()="ResponseCode"]') as $n) { $rc = trim($n->nodeValue); break; }
    }
    return ['ok' => ($rc === 'NoError'), 'code' => ($rc !== '' ? $rc : 'unknown')];
}

// ---- 1) все активные юзеры Б24 с email ----
echo "Собираю активных пользователей Битрикс24…\n";
$b24 = new BitrixClient($cfg['bitrix']['webhook']);
$users = [];
$start = 0;
try {
    for ($page = 0; $page < 100; $page++) {
        $res = $b24->call('user.get', ['FILTER' => ['ACTIVE' => true], 'start' => $start]);
        if (!is_array($res) || count($res) === 0) { break; }
        foreach ($res as $u) {
            $em = trim((string)($u['EMAIL'] ?? ''));
            if ($em === '' || !str_contains($em, '@')) { continue; }
            $key = strtolower($em);
            $users[$key] = [
                'id'    => (int)($u['ID'] ?? 0),
                'email' => $em,
                'name'  => trim(((string)($u['NAME'] ?? '')) . ' ' . ((string)($u['LAST_NAME'] ?? ''))),
            ];
        }
        if (count($res) < 50) { break; }
        $start += 50;
    }
} catch (\Throwable $e) {
    echo "  ОШИБКА Б24: " . $e->getMessage() . "\n";
}
echo "  юзеров с email: " . count($users) . "\n\n";

// ---- 2) по каждому email — факт-проверка ящика в Exchange ----
echo "Проверяю ящики в Exchange по email (делегатный GetFolder)…\n";
$matched = [];
$noMbx   = [];
$i = 0; $n = count($users);
foreach ($users as $u) {
    $i++;
    $r = mailboxProbe($endpoint, $ex['service_user'], $ex['service_pass'], $version, $u['email']);
    if ($r['ok']) {
        $matched[] = $u;
    } else {
        $noMbx[] = $u + ['reason' => $r['code']];
    }
    if ($i % 20 === 0) { fwrite(STDERR, "  …$i/$n\n"); }
    usleep(120000); // ~8 req/сек, чтобы не долбить EWS
}

// ---- вывод ----
$show = function (string $t, array $rows, callable $f) {
    echo "── $t (" . count($rows) . ") ──\n";
    $k = 0;
    foreach ($rows as $r) { if ($k++ >= 20) { echo "  … ещё " . (count($rows) - 20) . "\n"; break; } echo "  " . $f($r) . "\n"; }
    echo "\n";
};
echo "\n==================== ИТОГ ====================\n\n";
$show('MATCHED — email = живой ящик Exchange (в sync_users)', $matched,
    fn($r) => sprintf('%-34s Б24#%-6d %s', $r['email'], $r['id'], $r['name']));
$show('NO-MAILBOX — у email Б24 нет ящика/недоступен', $noMbx,
    fn($r) => sprintf('%-34s %-24s %s', $r['email'], $r['reason'], $r['name']));

// ---- CSV + готовые INSERT для matched ----
$fh = fopen($csvPath, 'w');
fwrite($fh, "\xEF\xBB\xBF");
fputcsv($fh, ['status', 'email', 'b24_id', 'name', 'reason']);
foreach ($matched as $r) { fputcsv($fh, ['matched',    $r['email'], $r['id'], $r['name'], '']); }
foreach ($noMbx as $r)   { fputcsv($fh, ['no-mailbox', $r['email'], $r['id'], $r['name'], $r['reason']]); }
fclose($fh);

$sqlPath = preg_replace('/\.csv$/', '.sql', $csvPath) ?: $csvPath . '.sql';
$sh = fopen($sqlPath, 'w');
fwrite($sh, "-- Кандидаты в sync_users (email = ews_smtp). Проверь и выполни в phpMyAdmin.\n");
foreach ($matched as $r) {
    $e = str_replace("'", "''", $r['email']);
    fwrite($sh, "INSERT INTO sync_users (email, ews_smtp, active) VALUES ('$e', '$e', 1);\n");
}
fclose($sh);

echo "CSV: $csvPath\n";
echo "Готовые INSERT для matched: $sqlPath  (проверь глазами перед импортом)\n";
echo "Ничего не записано ни в календари, ни в БД.\n";
