<?php
// ============================================================
// LIFE CRM — Telegram Bot (bot.php)
// Установить webhook: https://api.telegram.org/bot{TOKEN}/setWebhook?url=https://yourdomain.com/path/bot.php
// ============================================================

define('DATA_DIR', __DIR__ . '/data/');
define('BOT_TOKEN', '8702602382:AAHXhRb3pvYSC1ws6XmqOZcEnRwwsLndSIw');        // ← вставить токен бота
define('ALLOWED_CHAT_ID', '8248278786'); // ← вставить свой chat_id (получить у @userinfobot)
define('GROQ_API_KEY', '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');    // ← вставить Groq API key (console.groq.com)

if (!is_dir(DATA_DIR)) mkdir(DATA_DIR, 0755, true);

// ---- Emergency error logger (пишет в data/_error.log) -------
@ini_set('display_errors', '0');
@ini_set('log_errors', '1');
@ini_set('error_log', DATA_DIR . '_error.log');
register_shutdown_function(function() {
    $e = error_get_last();
    if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR])) {
        @file_put_contents(DATA_DIR . '_error.log',
            '['.date('Y-m-d H:i:s')."] FATAL: ".$e['message']." in ".$e['file'].":".$e['line']."\n",
            FILE_APPEND);
    }
});
set_exception_handler(function($ex) {
    @file_put_contents(DATA_DIR . '_error.log',
        '['.date('Y-m-d H:i:s')."] EXCEPTION: ".$ex->getMessage()." in ".$ex->getFile().":".$ex->getLine()."\n".$ex->getTraceAsString()."\n",
        FILE_APPEND);
});

// ---- Storage ------------------------------------------------
function readData($name) {
    $file = DATA_DIR . $name . '.json';
    if (!file_exists($file)) return [];
    $d = json_decode(file_get_contents($file), true);
    return is_array($d) ? $d : [];
}
function writeData($name, $data) {
    file_put_contents(DATA_DIR . $name . '.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
function readSettings() {
    $file = DATA_DIR . 'settings.json';
    if (!file_exists($file)) return [];
    $d = json_decode(file_get_contents($file), true);
    return is_array($d) && !isset($d[0]) ? $d : [];
}
function genId() { return base_convert(time(), 10, 36) . base_convert(rand(0, 46655), 10, 36); }
function todayDate() { return date('Y-m-d'); }
function fmtMoney($n) { return number_format((float)$n, 2, '.', ' ') . ' $'; }
function fmtDate($d) { if (!$d) return ''; $dt = DateTime::createFromFormat('Y-m-d', $d); return $dt ? $dt->format('d.m.Y') : $d; }
function daysLeft($d) { if (!$d) return null; return (int)ceil((strtotime($d) - time()) / 86400); }
function daysAgo($d) { if (!$d) return 0; return (int)floor((time() - strtotime($d)) / 86400); }

function normCur($cur, $fallback = 'USD') {
    $cur = strtoupper(trim((string)$cur));
    return in_array($cur, ['USD','AED','RUB'], true) ? $cur : $fallback;
}

function fxRates() {
    $s = readSettings();
    $usd = (float)($s['rateUSD'] ?? 90);
    $aed = (float)($s['rateAED'] ?? 24.5);
    if ($usd <= 0) $usd = 90;
    if ($aed <= 0) $aed = 24.5;
    return ['RUB' => 1.0, 'USD' => $usd, 'AED' => $aed];
}

function toRubMoney($amount, $cur, $rates) {
    $cur = normCur($cur, 'USD');
    $n = (float)str_replace(',', '.', (string)$amount);
    if (!is_finite($n)) $n = 0.0;
    return $n * ($rates[$cur] ?? 1.0);
}

function rubToUsdMoney($rub, $rates) {
    $usd = (float)($rates['USD'] ?? 90);
    if ($usd <= 0) $usd = 90;
    return ((float)$rub) / $usd;
}

function finUsd($f, $rates) {
    if (isset($f['amounts']) && is_array($f['amounts'])) {
        // Multi-currency fields describe THE SAME payment in different currencies
        // (equivalents, not additive). Prefer the saved USD amount so the historical
        // FX rate stays locked in; only fall back to live conversion if USD is missing.
        if (($f['amounts']['USD'] ?? '') !== '') return (float)str_replace(',', '.', (string)$f['amounts']['USD']);
        if (($f['amounts']['RUB'] ?? '') !== '') return rubToUsdMoney((float)str_replace(',', '.', (string)$f['amounts']['RUB']), $rates);
        if (($f['amounts']['AED'] ?? '') !== '') {
            $rub = toRubMoney($f['amounts']['AED'], 'AED', $rates);
            return rubToUsdMoney($rub, $rates);
        }
    }
    $cur = normCur($f['currency'] ?? 'USD', 'USD');
    if ($cur === 'USD') return (float)str_replace(',', '.', (string)($f['amount'] ?? 0));
    $rub = toRubMoney($f['amount'] ?? 0, $cur, $rates);
    return rubToUsdMoney($rub, $rates);
}

// ---- State (multi-step input) --------------------------------
function getState($chatId) {
    $states = readData('_bot_states');
    return $states[$chatId] ?? null;
}
function setState($chatId, $state) {
    $states = readData('_bot_states');
    if ($state === null) unset($states[$chatId]);
    else $states[$chatId] = $state;
    writeData('_bot_states', $states);
}

// ---- Telegram API -------------------------------------------
function tg($method, $params) {
    $url = 'https://api.telegram.org/bot' . BOT_TOKEN . '/' . $method;
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($params),
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
    ]);
    $result = curl_exec($ch);
    curl_close($ch);
    return json_decode($result, true);
}

function sendMsg($chatId, $text, $keyboard = null) {
    $params = ['chat_id' => $chatId, 'text' => $text, 'parse_mode' => 'HTML'];
    if ($keyboard) $params['reply_markup'] = json_encode($keyboard);
    return tg('sendMessage', $params);
}

function editMsg($chatId, $msgId, $text, $keyboard = null) {
    $params = ['chat_id' => $chatId, 'message_id' => $msgId, 'text' => $text, 'parse_mode' => 'HTML'];
    if ($keyboard) $params['reply_markup'] = json_encode($keyboard);
    return tg('editMessageText', $params);
}

function answerCallback($callbackId, $text = '') {
    tg('answerCallbackQuery', ['callback_query_id' => $callbackId, 'text' => $text]);
}

// ---- Keyboards ----------------------------------------------
function mainMenu() {
    return ['inline_keyboard' => [
        [['text' => '👥 Клиенты',  'callback_data' => 'menu_clients'],
         ['text' => '💰 Финансы',  'callback_data' => 'menu_finances']],
        [['text' => '✅ Задачи',   'callback_data' => 'menu_tasks'],
         ['text' => '📝 Заметки',  'callback_data' => 'menu_notes']],
        [['text' => '📈 Трекер',   'callback_data' => 'menu_tracker'],
         ['text' => '📊 Сводка',   'callback_data' => 'menu_summary']],
    ]];
}

function backMenu() {
    return ['inline_keyboard' => [[['text' => '← Главное меню', 'callback_data' => 'menu_main']]]];
}

function clientsMenu() {
    return ['inline_keyboard' => [
        [['text' => '➕ Новый клиент',    'callback_data' => 'client_add']],
        [['text' => '📋 Список клиентов', 'callback_data' => 'client_list']],
        [['text' => '🔔 Кто не пополнил', 'callback_data' => 'client_push']],
        [['text' => '🗑 Удалить клиента', 'callback_data' => 'client_delete_list']],
        [['text' => '← Назад',            'callback_data' => 'menu_main']],
    ]];
}

function financesMenu() {
    return ['inline_keyboard' => [
        [['text' => '💚 Доход',          'callback_data' => 'fin_add_income'],
         ['text' => '❤️ Расход',         'callback_data' => 'fin_add_expense']],
        [['text' => '🟡 Долг (я должен)', 'callback_data' => 'fin_add_debt_out'],
         ['text' => '🔵 Долг (мне)',      'callback_data' => 'fin_add_debt_in']],
        [['text' => '📋 Список',          'callback_data' => 'fin_list'],
         ['text' => '📅 По дням',         'callback_data' => 'fin_by_day']],
        [['text' => '← Назад',            'callback_data' => 'menu_main']],
    ]];
}

function tasksMenu() {
    return ['inline_keyboard' => [
        [['text' => '➕ Новая задача',    'callback_data' => 'task_add']],
        [['text' => '📋 Активные задачи', 'callback_data' => 'task_list']],
        [['text' => '⚠️ Просроченные',   'callback_data' => 'task_overdue']],
        [['text' => '← Назад',            'callback_data' => 'menu_main']],
    ]];
}

function trackerMenu() {
    return ['inline_keyboard' => [
        [['text' => '✏️ Заполнить сегодня', 'callback_data' => 'tracker_fill']],
        [['text' => '📅 Последние 7 дней',  'callback_data' => 'tracker_history']],
        [['text' => '← Назад',              'callback_data' => 'menu_main']],
    ]];
}

function clientStatusKb($clientId) {
    $statuses = [
        'lead' => 'Лид', 'card_opened' => 'Карта открыта',
        'funded' => 'Пополнил', 'active' => 'Активен', 'cold' => 'Холодный'
    ];
    $rows = [];
    foreach ($statuses as $k => $v) {
        $rows[] = [['text' => $v, 'callback_data' => "client_status_{$clientId}_{$k}"]];
    }
    $rows[] = [['text' => '← Клиенты', 'callback_data' => 'menu_clients']];
    return ['inline_keyboard' => $rows];
}

function clientDeleteKb($clients) {
    $rows = [];
    foreach (array_slice($clients, 0, 10) as $c) {
        $rows[] = [['text' => '🗑 ' . $c['name'] . ' (' . ($c['status']??'') . ')', 'callback_data' => 'client_del_' . $c['id']]];
    }
    $rows[] = [['text' => '← Назад', 'callback_data' => 'menu_clients']];
    return ['inline_keyboard' => $rows];
}

function taskDoneKb($tasks) {
    $rows = [];
    foreach (array_slice($tasks, 0, 8) as $t) {
        $rows[] = [['text' => '✅ ' . mb_substr($t['title'], 0, 40), 'callback_data' => 'task_done_' . $t['id']]];
    }
    $rows[] = [['text' => '← Назад', 'callback_data' => 'menu_tasks']];
    return ['inline_keyboard' => $rows];
}

// ---- Content builders ---------------------------------------
function buildSummary() {
    // Старая точка входа (используется в cron и при /start)
    // По умолчанию показываем сводку за сегодня.
    [$text, $kb] = buildSummaryToday();
    return $text;
}

// Клавиатура переключения вид сводки: сегодня ↔ месяц
function summaryKb($current = 'today') {
    $todayBtn = ['text' => ($current==='today' ? '✓ Сегодня' : '📆 Сегодня'), 'callback_data' => 'sum_today'];
    $monthBtn = ['text' => ($current==='month' ? '✓ За месяц' : '📅 За месяц'), 'callback_data' => 'sum_month'];
    return ['inline_keyboard' => [
        [$todayBtn, $monthBtn],
        [['text' => '← Главное меню', 'callback_data' => 'menu_main']],
    ]];
}

// Финансовый блок за период: $items уже отфильтрован по датам
function finBlock($items, $rates) {
    $notPaid = array_values(array_filter($items, fn($f) => !($f['paid'] ?? false)));
    $income   = array_sum(array_map(fn($f) => ($f['type']??'')==='income'  ? finUsd($f, $rates) : 0, $notPaid));
    $expenses = array_sum(array_map(fn($f) => ($f['type']??'')==='expense' ? finUsd($f, $rates) : 0, $notPaid));
    $debts    = array_sum(array_map(fn($f) => ($f['type']??'')==='debt_out' && !($f['paid']??false) ? finUsd($f, $rates) : 0, $items));
    return [$income, $expenses, $debts];
}

// СВОДКА ЗА СЕГОДНЯ
function buildSummaryToday() {
    $clients  = readData('clients');
    $finances = readData('finances');
    $tasks    = readData('tasks');
    $tracker  = readData('tracker');
    $rates = fxRates();

    $today = todayDate();
    $todayFin = array_values(array_filter($finances, fn($f) => ($f['date']??($f['created']??'')) === $today));
    [$income, $expenses, $debts] = finBlock($todayFin, $rates);

    $pushClients = array_filter($clients, fn($c) => $c['status']==='card_opened' && daysAgo($c['updated']??$c['created']) >= 2);
    $activeTasks = array_filter($tasks, fn($t) => !($t['done']??false));
    $overdueTasks = array_filter($tasks, fn($t) => !($t['done']??false) && ($t['due']??'') && daysLeft($t['due']) < 0);
    $overdueFinances = array_filter($finances, fn($f) => ($f['due']??'') && !($f['paid']??false) && daysLeft($f['due']) < 0);

    $todayEntry = null;
    foreach ($tracker as $e) { if (($e['date']??'') === $today) { $todayEntry = $e; break; } }

    $text = "📊 <b>Сводка за сегодня</b>\n<i>" . date('d.m.Y') . "</i>\n\n";
    $text .= "💰 <b>Финансы за день</b>\n";
    $text .= "  💚 Доходы: " . fmtMoney($income) . "\n";
    $text .= "  ❤️ Расходы: " . fmtMoney($expenses) . "\n";
    $text .= "  💼 Баланс: <b>" . fmtMoney($income - $expenses) . "</b>\n";
    $text .= "  📋 Операций: " . count($todayFin) . "\n";

    $text .= "\n👥 <b>Клиенты</b>: " . count($clients) . " всего\n";
    if (count($pushClients) > 0) {
        $text .= "  🔔 Нужен push: " . count($pushClients) . "\n";
        foreach ($pushClients as $c) $text .= "    • " . $c['name'] . " (" . daysAgo($c['updated']??$c['created']) . "д)\n";
    }

    $text .= "\n✅ <b>Задачи</b>: " . count($activeTasks) . " активных\n";
    if (count($overdueTasks) > 0) {
        $text .= "  ⚠️ Просрочено: " . count($overdueTasks) . "\n";
        foreach (array_slice($overdueTasks, 0, 3) as $t) $text .= "    • " . $t['title'] . "\n";
    }

    if (count($overdueFinances) > 0) {
        $text .= "\n⚠️ <b>Просроченные платежи</b>:\n";
        foreach ($overdueFinances as $f) $text .= "  • " . $f['name'] . " — " . fmtMoney(finUsd($f, $rates)) . "\n";
    }

    if ($todayEntry) {
        $text .= "\n📈 <b>Трекер</b>: настроение " . ($todayEntry['mood']??'?') . "/10";
        if ($todayEntry['income']??'') $text .= ", доход " . fmtMoney($todayEntry['income']);
        $text .= "\n";
    } else {
        $text .= "\n📈 Трекер за сегодня не заполнен\n";
    }

    return [$text, summaryKb('today')];
}

// СВОДКА ЗА МЕСЯЦ (текущий)
function buildSummaryMonth() {
    $finances = readData('finances');
    $tasks    = readData('tasks');
    $clients  = readData('clients');
    $rates = fxRates();

    $monthKey = date('Y-m');
    $monthFin = array_values(array_filter($finances, fn($f) => strpos(($f['date']??($f['created']??'')), $monthKey) === 0));
    [$income, $expenses, $debts] = finBlock($monthFin, $rates);

    // Активные дни и среднее
    $activeDays = [];
    foreach ($monthFin as $f) {
        $d = $f['date'] ?? ($f['created'] ?? '');
        if ($d) $activeDays[$d] = true;
    }
    $daysCount = count($activeDays);
    $avg = $daysCount > 0 ? $income / $daysCount : 0;

    // Лучший / худший день месяца по ДОХОДАМ
    $byDay = [];
    foreach ($monthFin as $f) {
        if ($f['paid'] ?? false) continue;
        if (($f['type']??'') !== 'income') continue;
        $d = $f['date'] ?? ($f['created'] ?? '');
        if (!$d) continue;
        $byDay[$d] = ($byDay[$d] ?? 0) + finUsd($f, $rates);
    }
    $best = $worst = null;
    foreach ($byDay as $d => $v) {
        if ($best === null  || $v > $byDay[$best])  $best = $d;
        if ($worst === null || $v < $byDay[$worst]) $worst = $d;
    }

    // Топ-3 категории расходов
    $byCat = [];
    foreach ($monthFin as $f) {
        if (($f['type']??'') !== 'expense' || ($f['paid']??false)) continue;
        $c = $f['category'] ?: '🔧 Прочее';
        $byCat[$c] = ($byCat[$c] ?? 0) + finUsd($f, $rates);
    }
    arsort($byCat);

    $monthNames = ['','Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'];
    $mName = $monthNames[(int)date('n')] . ' ' . date('Y');

    $text = "📊 <b>Сводка за месяц</b>\n<i>{$mName}</i>\n\n";
    $text .= "💰 <b>Финансы</b>\n";
    $text .= "  💚 Доходы: " . fmtMoney($income) . "\n";
    $text .= "  ❤️ Расходы: " . fmtMoney($expenses) . "\n";
    $text .= "  💼 Баланс: <b>" . fmtMoney($income - $expenses) . "</b>\n";
    if ($debts > 0) $text .= "  ⚠️ Долги: " . fmtMoney($debts) . "\n";
    $text .= "  📋 Операций: " . count($monthFin) . " за {$daysCount} дн.\n";
    $text .= "  📈 Доход в среднем/день: " . fmtMoney($avg) . "\n";

    if ($best && $best !== $worst) {
        $text .= "\n🏆 Лучший день: " . date('d.m', strtotime($best)) . " (" . fmtMoney($byDay[$best]) . ")\n";
        $text .= "📉 Худший день: " . date('d.m', strtotime($worst)) . " (" . fmtMoney($byDay[$worst]) . ")\n";
    }

    if (count($byCat) > 0) {
        $text .= "\n🏷 <b>Топ категорий расходов</b>\n";
        $i = 0;
        foreach ($byCat as $cat => $sum) {
            if ($i++ >= 3) break;
            $pct = $expenses > 0 ? round($sum / $expenses * 100) : 0;
            $text .= "  • " . htmlspecialchars($cat) . " — " . fmtMoney($sum) . " ({$pct}%)\n";
        }
    }

    $text .= "\n👥 Клиентов всего: " . count($clients) . "\n";

    return [$text, summaryKb('month')];
}

function buildClientList() {
    $clients = readData('clients');
    if (!$clients) return '👥 Клиентов пока нет.';
    $labels = ['lead'=>'Лид','card_opened'=>'Карта','funded'=>'Пополнил','active'=>'Активен','cold'=>'Холодный'];
    $text = "👥 <b>Клиенты (" . count($clients) . ")</b>\n\n";
    foreach (array_slice($clients, 0, 15) as $c) {
        $status = $labels[$c['status']??''] ?? ($c['status']??'');
        $text .= "• <b>" . htmlspecialchars($c['name']) . "</b> — " . $status;
        if ($c['phone']??'') $text .= "\n  " . htmlspecialchars($c['phone']);
        if ($c['project']??'') $text .= " | " . htmlspecialchars($c['project']);
        $text .= "\n";
    }
    return $text;
}

function buildTaskList($onlyOverdue = false) {
    $tasks = readData('tasks');
    $active = array_values(array_filter($tasks, fn($t) => !($t['done']??false)));
    if ($onlyOverdue) $active = array_values(array_filter($active, fn($t) => ($t['due']??'') && daysLeft($t['due']) < 0));
    if (!$active) return $onlyOverdue ? '✅ Просроченных задач нет!' : '✅ Активных задач нет!';
    $text = ($onlyOverdue ? "⚠️ <b>Просроченные задачи</b>\n\n" : "✅ <b>Активные задачи (" . count($active) . ")</b>\n\n");
    foreach (array_slice($active, 0, 15) as $t) {
        $dl = daysLeft($t['due']??'');
        $text .= "• " . htmlspecialchars($t['title']);
        if ($t['project']??'') $text .= " <i>(" . htmlspecialchars($t['project']) . ")</i>";
        if ($dl !== null) $text .= $dl < 0 ? " ⚠️ просрочено" : " [" . $dl . "д]";
        $text .= "\n";
    }
    return $text;
}

function buildFinList() {
    $finances = readData('finances');
    if (!$finances) return '💰 Записей нет.';
    $rates = fxRates();

    // Баланс — по ВСЕМ записям (в синхроне со сводкой)
    $notPaid = array_values(array_filter($finances, fn($f) => !($f['paid'] ?? false)));
    $income  = array_sum(array_map(fn($f) => ($f['type']??'')==='income'  ? finUsd($f, $rates) : 0, $notPaid));
    $expense = array_sum(array_map(fn($f) => ($f['type']??'')==='expense' ? finUsd($f, $rates) : 0, $notPaid));

    $text = "💰 <b>Финансы</b>\n\n";
    $total = count($finances);
    foreach (array_slice($finances, 0, 10) as $f) {
        $type = ['income'=>'💚','expense'=>'❤️','debt_out'=>'🟡','debt_in'=>'🔵'][$f['type']??''] ?? '•';
        $usd = finUsd($f, $rates);
        $text .= $type . " " . htmlspecialchars($f['name']) . " — " . fmtMoney($usd);
        if ($f['paid']??false) $text .= " ✓";
        $text .= "\n";
    }
    if ($total > 10) $text .= "\n<i>… показаны 10 последних из " . $total . "</i>\n";
    $text .= "\n<b>Баланс: " . fmtMoney($income - $expense) . "</b>";
    return $text;
}

// ---- Финансы по дням ----
// Возвращает [text, keyboard] — список дней с суммарными цифрами; каждый день — кнопка.
function buildFinByDay($page = 0) {
    $finances = readData('finances');
    if (!$finances) return ['💰 Записей нет.', null];
    $rates = fxRates();

    // Группируем по дате
    $byDay = [];
    foreach ($finances as $f) {
        $d = $f['date'] ?? ($f['created'] ?? '');
        if (!$d) continue;
        if (!isset($byDay[$d])) $byDay[$d] = ['income'=>0, 'expense'=>0, 'count'=>0];
        $usd = finUsd($f, $rates);
        $paid = $f['paid'] ?? false;
        if (!$paid) {
            if (($f['type']??'')==='income')  $byDay[$d]['income']  += $usd;
            if (($f['type']??'')==='expense') $byDay[$d]['expense'] += $usd;
        }
        $byDay[$d]['count']++;
    }
    // Сортировка: новые сверху
    krsort($byDay);

    $days = array_keys($byDay);
    $perPage = 14;
    $totalPages = max(1, (int)ceil(count($days) / $perPage));
    $page = max(0, min($page, $totalPages - 1));
    $slice = array_slice($days, $page * $perPage, $perPage);

    $text = "📅 <b>Финансы по дням</b>\n";
    if ($totalPages > 1) $text .= "<i>Страница " . ($page + 1) . " из {$totalPages}</i>\n";
    $text .= "\n";

    $kb = [];
    foreach ($slice as $d) {
        $row = $byDay[$d];
        $net = $row['income'] - $row['expense'];
        $sign = $net >= 0 ? '🟢' : '🔴';
        $human = finDayLabel($d);
        $line = sprintf("%s <b>%s</b> — +%s / −%s = %s",
            $sign, $human,
            fmtMoney($row['income']), fmtMoney($row['expense']), fmtMoney($net));
        $text .= $line . "\n";
        $kb[] = [['text' => "📂 {$human} ({$row['count']})", 'callback_data' => "fin_day_{$d}_p{$page}"]];
    }

    // Пагинация
    $nav = [];
    if ($page > 0)               $nav[] = ['text'=>'⬅️', 'callback_data'=>'fin_by_day_p'.($page-1)];
    if ($page < $totalPages - 1) $nav[] = ['text'=>'➡️', 'callback_data'=>'fin_by_day_p'.($page+1)];
    if ($nav) $kb[] = $nav;

    $kb[] = [['text'=>'← К финансам', 'callback_data'=>'menu_finances']];
    return [$text, ['inline_keyboard' => $kb]];
}

// Детализация по конкретному дню
function buildFinDayDetail($date, $backPage = 0) {
    $finances = readData('finances');
    $rates = fxRates();
    $items = array_values(array_filter($finances, fn($f) => ($f['date']??($f['created']??'')) === $date));
    if (!$items) return ["💰 На {$date} записей нет.", ['inline_keyboard'=>[[['text'=>'← Назад', 'callback_data'=>'fin_by_day_p'.$backPage]]]]];

    $income = $expense = 0;
    $text = "📅 <b>" . finDayLabel($date) . "</b>\n<i>{$date}</i>\n\n";
    foreach ($items as $f) {
        $icon = ['income'=>'💚','expense'=>'❤️','debt_out'=>'🟡','debt_in'=>'🔵'][$f['type']??''] ?? '•';
        $usd = finUsd($f, $rates);
        $text .= $icon . " " . htmlspecialchars($f['name']) . " — " . fmtMoney($usd);
        if ($f['paid']??false) $text .= " ✓";
        $text .= "\n";
        if (!($f['paid']??false)) {
            if (($f['type']??'')==='income')  $income  += $usd;
            if (($f['type']??'')==='expense') $expense += $usd;
        }
    }
    $net = $income - $expense;
    $text .= "\n💚 Доходы:  " . fmtMoney($income);
    $text .= "\n❤️ Расходы: " . fmtMoney($expense);
    $text .= "\n<b>Итог: " . fmtMoney($net) . "</b>";

    $kb = ['inline_keyboard' => [
        [['text'=>'← К списку дней', 'callback_data'=>'fin_by_day_p'.$backPage]],
        [['text'=>'🏠 Главное меню', 'callback_data'=>'menu_main']],
    ]];
    return [$text, $kb];
}

// "Сегодня" / "Вчера" / "19 мая, вт"
function finDayLabel($date) {
    $today = date('Y-m-d');
    $yest  = date('Y-m-d', strtotime('-1 day'));
    if ($date === $today) return 'Сегодня';
    if ($date === $yest)  return 'Вчера';
    $ts = strtotime($date);
    if (!$ts) return $date;
    $months = ['','янв','фев','мар','апр','мая','июн','июл','авг','сен','окт','ноя','дек'];
    $wd = ['вс','пн','вт','ср','чт','пт','сб'];
    return (int)date('j', $ts) . ' ' . $months[(int)date('n', $ts)] . ', ' . $wd[(int)date('w', $ts)];
}


function buildPushList() {
    $clients = readData('clients');
    $push = array_values(array_filter($clients, fn($c) => $c['status']==='card_opened' && daysAgo($c['updated']??$c['created']) >= 2));
    if (!$push) return '✅ Все клиенты пополнили карты!';
    $text = "🔔 <b>Нужен push (" . count($push) . ")</b>\n\n";
    foreach ($push as $c) {
        $text .= "• <b>" . htmlspecialchars($c['name']) . "</b> — " . daysAgo($c['updated']??$c['created']) . " дн. без пополнения\n";
        if ($c['phone']??'') $text .= "  " . htmlspecialchars($c['phone']) . "\n";
    }
    return $text;
}

// ---- Input request helpers ----------------------------------
function askInput($chatId, $stateKey, $promptText, $keyboard = null) {
    setState($chatId, ['step' => $stateKey]);
    sendMsg($chatId, $promptText, $keyboard ?: ['remove_keyboard' => true]);
}

// ---- Process callback ---------------------------------------
function processCallback($chatId, $msgId, $callbackId, $data) {
    answerCallback($callbackId);
    setState($chatId, null); // clear state on menu action

    // Main menu
    if ($data === 'menu_main') {
        editMsg($chatId, $msgId, "🏠 <b>Life CRM</b>\nВыбери раздел:", mainMenu());
        return;
    }

    // Section menus
    if ($data === 'menu_clients') { editMsg($chatId, $msgId, "👥 <b>Клиенты</b>", clientsMenu()); return; }
    if ($data === 'menu_finances') { editMsg($chatId, $msgId, "💰 <b>Финансы</b>", financesMenu()); return; }
    if ($data === 'menu_tasks')   { editMsg($chatId, $msgId, "✅ <b>Задачи</b>", tasksMenu()); return; }
    if ($data === 'menu_notes')   {
        sendMsg($chatId, "📝 Введи текст заметки (можно с заголовком через |):\n\n<i>Пример: Идея | Сделать лендинг для клиента</i>\nИли просто текст без заголовка.");
        setState($chatId, ['step' => 'note_text']);
        return;
    }
    if ($data === 'menu_tracker') { editMsg($chatId, $msgId, "📈 <b>Трекер</b>", trackerMenu()); return; }
    if ($data === 'menu_summary' || $data === 'sum_today') {
        [$txt, $kb] = buildSummaryToday();
        editMsg($chatId, $msgId, $txt, $kb);
        return;
    }
    if ($data === 'sum_month') {
        [$txt, $kb] = buildSummaryMonth();
        editMsg($chatId, $msgId, $txt, $kb);
        return;
    }

    // Clients
    if ($data === 'client_list') { editMsg($chatId, $msgId, buildClientList(), backMenu()); return; }
    if ($data === 'client_delete_list') {
        $clients = readData('clients');
        if (!$clients) { editMsg($chatId, $msgId, "👥 Клиентов нет.", backMenu()); return; }
        editMsg($chatId, $msgId, "🗑 <b>Выбери клиента для удаления:</b>", clientDeleteKb($clients));
        return;
    }
    if (preg_match('/^client_del_(.+)$/', $data, $m)) {
        $clients = readData('clients');
        $name = '';
        foreach ($clients as $c) { if (($c['id']??'') === $m[1]) { $name = $c['name']; break; } }
        $clients = array_values(array_filter($clients, fn($c) => ($c['id']??'') !== $m[1]));
        writeData('clients', $clients);
        // also remove linked finance record (if exists)
        $finances = readData('finances');
        $finances = array_values(array_filter($finances, fn($f) => !(($f['sourceEntity'] ?? '') === 'clients' && ($f['sourceId'] ?? '') === $m[1])));
        writeData('finances', $finances);
        editMsg($chatId, $msgId, "✅ Клиент <b>" . htmlspecialchars($name) . "</b> удалён.", backMenu());
        return;
    }
    if ($data === 'client_push') { editMsg($chatId, $msgId, buildPushList(), backMenu()); return; }
    if ($data === 'client_add') {
        sendMsg($chatId, "👤 Введи имя клиента:");
        setState($chatId, ['step' => 'client_name']);
        return;
    }
    if (preg_match('/^client_status_(.+)_(lead|card_opened|funded|active|cold)$/', $data, $m)) {
        $clients = readData('clients');
        foreach ($clients as &$c) {
            if ($c['id'] === $m[1]) { $c['status'] = $m[2]; $c['updated'] = todayDate(); break; }
        }
        writeData('clients', $clients);
        $labels = ['lead'=>'Лид','card_opened'=>'Карта открыта','funded'=>'Пополнил','active'=>'Активен','cold'=>'Холодный'];
        editMsg($chatId, $msgId, "✅ Статус обновлён: <b>" . ($labels[$m[2]] ?? $m[2]) . "</b>", backMenu());
        return;
    }

    // Finances
    $finTypes = ['fin_add_income'=>'income','fin_add_expense'=>'expense','fin_add_debt_out'=>'debt_out','fin_add_debt_in'=>'debt_in'];
    if (isset($finTypes[$data])) {
        $typeLabels = ['income'=>'доход 💚','expense'=>'расход ❤️','debt_out'=>'долг (я должен) 🟡','debt_in'=>'долг (мне должны) 🔵'];
        $defCur = $finTypes[$data]==='expense' ? 'AED' : 'USD';
        sendMsg($chatId, "💰 Введи <b>" . $typeLabels[$finTypes[$data]] . "</b>\n\nФормат: <code>сумма [валюта] название</code>\nПример: <code>50 AED кофе</code>\nЕсли валюту не указать — по умолчанию <b>{$defCur}</b>.");
        setState($chatId, ['step' => 'fin_amount', 'type' => $finTypes[$data]]);
        return;
    }
    if ($data === 'fin_list') { editMsg($chatId, $msgId, buildFinList(), backMenu()); return; }

    // По дням: fin_by_day или fin_by_day_pN
    if ($data === 'fin_by_day' || preg_match('/^fin_by_day_p(\d+)$/', $data, $mp)) {
        $page = isset($mp[1]) ? (int)$mp[1] : 0;
        [$txt, $kb] = buildFinByDay($page);
        editMsg($chatId, $msgId, $txt, $kb ?: backMenu());
        return;
    }
    // Детализация одного дня: fin_day_YYYY-MM-DD_pN
    if (preg_match('/^fin_day_(\d{4}-\d{2}-\d{2})_p(\d+)$/', $data, $md)) {
        [$txt, $kb] = buildFinDayDetail($md[1], (int)$md[2]);
        editMsg($chatId, $msgId, $txt, $kb);
        return;
    }

    // Tasks
    if ($data === 'task_add') {
        sendMsg($chatId, "✅ Введи задачу:\n\nФормат: <code>Название задачи</code>\nИли с проектом: <code>Название | Проект</code>\nС дедлайном: <code>Название | Проект | 2026-05-25</code>");
        setState($chatId, ['step' => 'task_title']);
        return;
    }
    if ($data === 'task_list') {
        $tasks = readData('tasks');
        $active = array_values(array_filter($tasks, fn($t) => !($t['done']??false)));
        editMsg($chatId, $msgId, buildTaskList(), $active ? taskDoneKb($active) : backMenu());
        return;
    }
    if ($data === 'task_overdue') { editMsg($chatId, $msgId, buildTaskList(true), backMenu()); return; }
    if (preg_match('/^task_done_(.+)$/', $data, $m)) {
        $tasks = readData('tasks');
        $title = '';
        foreach ($tasks as &$t) {
            if ($t['id'] === $m[1]) { $t['done'] = true; $t['doneAt'] = todayDate(); $title = $t['title']; break; }
        }
        writeData('tasks', $tasks);
        editMsg($chatId, $msgId, "✅ Выполнено: <b>" . htmlspecialchars($title) . "</b>", backMenu());
        return;
    }

    // Tracker
    if ($data === 'tracker_fill') {
        sendMsg($chatId, "📈 Введи данные через запятую:\n\n<code>доход, настроение(1-10), сон(ч), спорт(мин)</code>\n\nПример: <code>3000, 7, 7.5, 30</code>\nМожно пропускать: <code>0, 8, 0, 0</code>");
        setState($chatId, ['step' => 'tracker_data']);
        return;
    }
    if ($data === 'tracker_history') {
        $tracker = readData('tracker');
        $text = "📅 <b>Последние 7 дней</b>\n\n";
        $count = 0;
        foreach ($tracker as $e) {
            if ($count >= 7) break;
            $dt = DateTime::createFromFormat('Y-m-d', $e['date']??'');
            $text .= "<b>" . ($dt ? $dt->format('d.m') : ($e['date']??'')) . "</b>: настроение " . ($e['mood']??'?') . "/10";
            if ($e['income']??'') $text .= ", доход " . fmtMoney($e['income']);
            if ($e['sleep']??'') $text .= ", сон " . $e['sleep'] . "ч";
            if ($e['sport']??'') $text .= ", спорт " . $e['sport'] . "м";
            $text .= "\n";
            $count++;
        }
        if (!$count) $text .= "Нет данных";
        editMsg($chatId, $msgId, $text, backMenu());
        return;
    }
}

// ---- Process text (multi-step input) -----------------------
function processText($chatId, $text) {
    $state = getState($chatId);

    if (!$state) {
        sendMsg($chatId, "🏠 <b>Life CRM</b>\nВыбери раздел:", mainMenu());
        return;
    }

    $step = $state['step'] ?? '';

    // ---- NOTE ----
    if ($step === 'note_text') {
        $parts = explode('|', $text, 2);
        $title = count($parts) > 1 ? trim($parts[0]) : '';
        $content = count($parts) > 1 ? trim($parts[1]) : trim($text);
        $notes = readData('notes');
        array_unshift($notes, ['id'=>genId(),'title'=>$title,'content'=>$content,'tag'=>'Личное','created'=>todayDate()]);
        writeData('notes', $notes);
        setState($chatId, null);
        sendMsg($chatId, "📝 Заметка сохранена" . ($title ? ": <b>" . htmlspecialchars($title) . "</b>" : "") . "!", mainMenu());
        return;
    }

    // ---- CLIENT ----
    if ($step === 'client_name') {
        setState($chatId, ['step' => 'client_phone', 'name' => $text]);
        sendMsg($chatId, "📱 Телефон или Telegram клиента (или /skip):");
        return;
    }
    if ($step === 'client_phone') {
        $phone = ($text === '/skip') ? '' : $text;
        setState($chatId, array_merge($state, ['step' => 'client_project', 'phone' => $phone]));
        sendMsg($chatId, "📁 Проект (например: Numio) или /skip:");
        return;
    }
    if ($step === 'client_project') {
        $project = ($text === '/skip') ? '' : $text;
        $clients = readData('clients');
        $client = ['id'=>genId(),'name'=>$state['name'],'phone'=>$state['phone']??'','project'=>$project,'status'=>'lead','note'=>'','amount'=>'','created'=>todayDate(),'updated'=>todayDate()];
        array_unshift($clients, $client);
        writeData('clients', $clients);
        setState($chatId, null);
        sendMsg($chatId, "✅ Клиент <b>" . htmlspecialchars($state['name']) . "</b> добавлен со статусом «Лид»\n\nПоменять статус:", clientStatusKb($client['id']));
        return;
    }

    // ---- FINANCE ----
    if ($step === 'fin_amount') {
        // Parse "50 AED кофе" or "50 кофе" (default currency depends on type)
        preg_match('/^(\d+(?:[\\.,]\\d+)?)\\s*(USD|AED|RUB)?\\s*(.*)$/iu', trim($text), $m);
        $amountRaw = $m[1] ?? '';
        $curRaw = $m[2] ?? '';
        $name = trim($m[3] ?? '') ?: 'Без названия';
        $amountNum = (float)str_replace(',', '.', (string)$amountRaw);
        if (!is_finite($amountNum) || $amountNum <= 0) { sendMsg($chatId, "⚠️ Не понял сумму. Пример: <code>50 AED кофе</code>"); return; }
        $defaultCur = ($state['type'] ?? '') === 'expense' ? 'AED' : 'USD';
        $currency = normCur($curRaw, $defaultCur);
        $amount = $currency === 'RUB' ? (string)round($amountNum) : number_format($amountNum, 2, '.', '');
        $rates = fxRates();
        $finances = readData('finances');
        $labels = ['income'=>'Доход','expense'=>'Расход','debt_out'=>'Долг (я должен)','debt_in'=>'Долг (мне должны)'];
        $item = [
            'id'=>genId(),
            'type'=>$state['type'],
            'name'=>$name,
            'category'=>'',
            'amount'=>$amount,
            'currency'=>$currency,
            'amounts'=>[$currency=>$amount],
            'date'=>todayDate(),
            'due'=>'',
            'note'=>'',
            'paid'=>false,
            'created'=>todayDate(),
        ];
        array_unshift($finances, $item);
        writeData('finances', $finances);
        setState($chatId, null);
        sendMsg($chatId, "✅ Записано: <b>" . ($labels[$state['type']]??'') . "</b>\n" . htmlspecialchars($name) . " — " . fmtMoney(finUsd($item, $rates)), mainMenu());
        return;
    }

    // ---- TASK ----
    if ($step === 'task_title') {
        $parts = array_map('trim', explode('|', $text));
        $title = $parts[0] ?? $text;
        $project = $parts[1] ?? '';
        $due = $parts[2] ?? '';
        $tasks = readData('tasks');
        array_unshift($tasks, ['id'=>genId(),'title'=>$title,'project'=>$project,'due'=>$due,'priority'=>'mid','note'=>'','done'=>false,'created'=>todayDate()]);
        writeData('tasks', $tasks);
        setState($chatId, null);
        $resp = "✅ Задача добавлена: <b>" . htmlspecialchars($title) . "</b>";
        if ($project) $resp .= "\n📁 Проект: " . htmlspecialchars($project);
        if ($due) $resp .= "\n📅 Дедлайн: " . fmtDate($due);
        sendMsg($chatId, $resp, mainMenu());
        return;
    }

    // ---- TRACKER ----
    if ($step === 'tracker_data') {
        $parts = array_map('trim', explode(',', $text));
        $income = (float)($parts[0] ?? 0);
        $mood   = (int)($parts[1] ?? 5);
        $sleep  = (float)($parts[2] ?? 0);
        $sport  = (float)($parts[3] ?? 0);
        $mood = max(1, min(10, $mood));

        $tracker = readData('tracker');
        $entry = ['id'=>genId(),'date'=>todayDate(),'income'=>$income?:"", 'mood'=>$mood,'sleep'=>$sleep?:"", 'sport'=>$sport?:"", 'note'=>''];

        $found = false;
        foreach ($tracker as &$e) {
            if (($e['date']??'') === todayDate()) { $e = array_merge($e, $entry); $found = true; break; }
        }
        if (!$found) array_unshift($tracker, $entry);
        writeData('tracker', $tracker);
        setState($chatId, null);

        $moodEmoji = $mood >= 8 ? '😄' : ($mood >= 5 ? '😐' : '😔');
        $resp = "📈 <b>Трекер сохранён!</b>\n";
        $resp .= "Настроение: $moodEmoji $mood/10\n";
        if ($income) $resp .= "Доход: " . fmtMoney($income) . "\n";
        if ($sleep) $resp .= "Сон: {$sleep}ч\n";
        if ($sport) $resp .= "Спорт: {$sport}мин\n";
        sendMsg($chatId, $resp, mainMenu());
        return;
    }

    // Unknown
    sendMsg($chatId, "🏠 <b>Life CRM</b>", mainMenu());
}

// ============================================================
// VOICE — transcribe + understand + act
// ============================================================

function downloadVoice($fileId) {
    // Get file path from Telegram
    $url = 'https://api.telegram.org/bot' . BOT_TOKEN . '/getFile?file_id=' . $fileId;
    $ch = curl_init($url);
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]);
    $res = json_decode(curl_exec($ch), true);
    curl_close($ch);

    $filePath = $res['result']['file_path'] ?? null;
    if (!$filePath) return null;

    // Download audio file
    $audioUrl = 'https://api.telegram.org/file/bot' . BOT_TOKEN . '/' . $filePath;
    $tmpFile = tempnam(sys_get_temp_dir(), 'voice_') . '.ogg';
    $ch = curl_init($audioUrl);
    $fp = fopen($tmpFile, 'wb');
    curl_setopt_array($ch, [CURLOPT_FILE => $fp, CURLOPT_TIMEOUT => 30, CURLOPT_FOLLOWLOCATION => true]);
    curl_exec($ch);
    curl_close($ch);
    fclose($fp);

    return file_exists($tmpFile) && filesize($tmpFile) > 0 ? $tmpFile : null;
}

function transcribeWithGroq($filePath) {
    if (!GROQ_API_KEY) return null;

    $ch = curl_init('https://api.groq.com/openai/v1/audio/transcriptions');
    $postFields = [
        'file'     => new CURLFile($filePath, 'audio/ogg', 'voice.ogg'),
        'model'    => 'whisper-large-v3-turbo',
        'language' => 'ru',
        'response_format' => 'json',
    ];
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $postFields,
        CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . GROQ_API_KEY],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
    ]);
    $res = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $res['text'] ?? null;
}

function understandWithClaude($text) {
    $settings = readSettings();
    $apiKey = $settings['apiKey'] ?? '';
    if (!$apiKey) return null;

    // Snapshot of current data for context
    $clients  = readData('clients');
    $finances = readData('finances');
    $tasks    = readData('tasks');

    $clientNames = implode(', ', array_slice(array_map(fn($c) => $c['name'], $clients), 0, 20));

    $prompt = <<<PROMPT
Ты обрабатываешь голосовые команды для личной CRM системы. Пользователь сказал:
"$text"

Текущие клиенты в CRM: $clientNames

Определи намерение и верни ТОЛЬКО JSON без пояснений. Форматы:

Добавить доход: {"action":"add_income","amount":5000,"currency":"USD","name":"фриланс"}
Добавить расход: {"action":"add_expense","amount":3000,"currency":"AED","name":"продукты"}
Добавить долг (я должен): {"action":"add_debt_out","amount":10000,"currency":"RUB","name":"Ивану"}
Добавить долг (мне должны): {"action":"add_debt_in","amount":5000,"currency":"USD","name":"Саша"}
Добавить клиента: {"action":"add_client","name":"Иван Петров","phone":"","project":"Numio","status":"lead"}
Обновить статус клиента: {"action":"update_client_status","name":"Иван","status":"funded"}
Добавить задачу: {"action":"add_task","title":"Позвонить Ивану","project":"Numio","due":"2026-05-25","priority":"high"} (priority: high/mid/low, due необязателен)
Добавить заметку: {"action":"add_note","title":"Идея","content":"текст заметки","tag":"Идея"} (теги: Работа/Личное/Идея/Клиент/Финансы/Астро)
Заполнить трекер: {"action":"add_tracker","income":5000,"mood":8,"sleep":7.5,"sport":30} (все поля необязательны кроме mood)
Получить сводку: {"action":"summary"}
Непонятная команда: {"action":"unknown","message":"уточняющее сообщение пользователю"}

Статусы клиента и их смысл:
- lead = просто лид, ещё ничего не сделал
- card_opened = открыл карту (синонимы: "открыл карту", "завёл карту", "зарегистрировался")
- funded = пополнил карту (синонимы: "пополнил", "внёс деньги", "закинул", "заплатил")
- active = активный клиент
- cold = холодный, неактивный

Правила:
- Суммы только цифрами (пять тысяч → 5000)
- Валюта (currency): USD/AED/RUB. Распознавай по словам: "доллар/бакс/USD" → USD; "дирхам/AED" → AED; "рубл/руб/RUB" → RUB. Если валюта не указана — для income используй USD, для expense — AED, для долгов — USD.
- Даты в формате YYYY-MM-DD, сегодня: CURRENTDATE
- Если в фразе есть имя + статус (например "Михаил открыл карту") — это update_client_status если клиент уже есть в списке, иначе add_client с нужным status
- Если сказано "создай клиента Михаил" или "добавь клиента" — это add_client
- Если клиент уже есть в списке и упоминается с новым статусом — это update_client_status
- Отвечай ТОЛЬКО JSON, никакого текста вокруг
PROMPT;

    $prompt = str_replace('CURRENTDATE', date('Y-m-d'), $prompt);

    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'x-api-key: ' . $apiKey,
            'anthropic-version: 2023-06-01',
        ],
        CURLOPT_POSTFIELDS     => json_encode([
            'model'      => 'claude-sonnet-4-20250514',
            'max_tokens' => 300,
            'messages'   => [['role' => 'user', 'content' => $prompt]],
        ]),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 20,
    ]);
    $res = json_decode(curl_exec($ch), true);
    curl_close($ch);

    $raw = $res['content'][0]['text'] ?? '';
    // Strip possible markdown fences
    $raw = preg_replace('/```(?:json)?\s*/', '', $raw);
    $raw = trim($raw, '` ');
    return json_decode($raw, true);
}

function executeVoiceAction($chatId, $action) {
    if (!$action) {
        sendMsg($chatId, "⚠️ Не удалось распознать команду. Попробуй ещё раз или используй меню.", mainMenu());
        return;
    }

    $type = $action['action'] ?? 'unknown';

    // ---- FINANCES ----
    if (in_array($type, ['add_income', 'add_expense', 'add_debt_out', 'add_debt_in'])) {
        $typeMap = ['add_income'=>'income','add_expense'=>'expense','add_debt_out'=>'debt_out','add_debt_in'=>'debt_in'];
        $labelMap = ['add_income'=>'Доход 💚','add_expense'=>'Расход ❤️','add_debt_out'=>'Долг (я должен) 🟡','add_debt_in'=>'Долг (мне должны) 🔵'];
        $amountNum = (float)str_replace(',', '.', (string)($action['amount'] ?? 0));
        $name   = $action['name'] ?? 'Без названия';
        if (!$amountNum) { sendMsg($chatId, "⚠️ Не понял сумму. Скажи: «запиши расход пять тысяч аренда»"); return; }
        $currency = normCur($action['currency'] ?? '', $typeMap[$type] === 'expense' ? 'AED' : 'USD');
        $amount = $currency === 'RUB' ? (string)round($amountNum) : number_format($amountNum, 2, '.', '');
        $rates = fxRates();
        $item = [
            'id'=>genId(),
            'type'=>$typeMap[$type],
            'name'=>$name,
            'category'=>'',
            'amount'=>$amount,
            'currency'=>$currency,
            'amounts'=>[$currency=>$amount],
            'date'=>todayDate(),
            'due'=>'',
            'note'=>'',
            'paid'=>false,
            'created'=>todayDate(),
        ];
        // Если запись не в USD — сразу зафиксируем USD-эквивалент, чтобы баланс не «плавал»
        if ($currency !== 'USD') {
            $item['amounts']['USD'] = number_format(finUsd($item, $rates), 2, '.', '');
        }
        $finances = readData('finances');
        array_unshift($finances, $item);
        writeData('finances', $finances);
        sendMsg($chatId, "✅ <b>" . $labelMap[$type] . "</b>\n" . htmlspecialchars($name) . " — " . fmtMoney(finUsd($item, $rates)), mainMenu());
        return;
    }

    // ---- CLIENT ADD ----
    if ($type === 'add_client') {
        $name = $action['name'] ?? '';
        if (!$name) { sendMsg($chatId, "⚠️ Не услышал имя клиента"); return; }
        $clients = readData('clients');
        $client = ['id'=>genId(),'name'=>$name,'phone'=>$action['phone']??'','project'=>$action['project']??'','status'=>'lead','note'=>'','amount'=>'','created'=>todayDate(),'updated'=>todayDate()];
        array_unshift($clients, $client);
        writeData('clients', $clients);
        $resp = "✅ Клиент <b>" . htmlspecialchars($name) . "</b> добавлен";
        if ($action['project']??'') $resp .= "\nПроект: " . htmlspecialchars($action['project']);
        if ($action['phone']??'') $resp .= "\nТелефон: " . htmlspecialchars($action['phone']);
        sendMsg($chatId, $resp . "\n\nПоменять статус:", clientStatusKb($client['id']));
        return;
    }

    // ---- CLIENT STATUS ----
    if ($type === 'update_client_status') {
        $searchName = mb_strtolower($action['name'] ?? '');
        $status = $action['status'] ?? 'active';
        $clients = readData('clients');
        $found = false;
        foreach ($clients as &$c) {
            if (mb_strpos(mb_strtolower($c['name']), $searchName) !== false) {
                $c['status'] = $status; $c['updated'] = todayDate(); $found = true; break;
            }
        }
        writeData('clients', $clients);
        $labels = ['lead'=>'Лид','card_opened'=>'Карта открыта','funded'=>'Пополнил','active'=>'Активен','cold'=>'Холодный'];
        if ($found) sendMsg($chatId, "✅ Статус клиента обновлён: <b>" . ($labels[$status]??$status) . "</b>", mainMenu());
        else sendMsg($chatId, "⚠️ Клиент «" . htmlspecialchars($action['name']??'') . "» не найден в CRM", mainMenu());
        return;
    }

    // ---- TASK ----
    if ($type === 'add_task') {
        $title = $action['title'] ?? '';
        if (!$title) { sendMsg($chatId, "⚠️ Не услышал название задачи"); return; }
        $tasks = readData('tasks');
        array_unshift($tasks, ['id'=>genId(),'title'=>$title,'project'=>$action['project']??'','due'=>$action['due']??'','priority'=>$action['priority']??'mid','note'=>'','done'=>false,'created'=>todayDate()]);
        writeData('tasks', $tasks);
        $resp = "✅ Задача: <b>" . htmlspecialchars($title) . "</b>";
        if ($action['project']??'') $resp .= "\n📁 " . htmlspecialchars($action['project']);
        if ($action['due']??'') $resp .= "\n📅 " . fmtDate($action['due']);
        sendMsg($chatId, $resp, mainMenu());
        return;
    }

    // ---- NOTE ----
    if ($type === 'add_note') {
        $content = $action['content'] ?? $action['title'] ?? '';
        if (!$content) { sendMsg($chatId, "⚠️ Пустая заметка"); return; }
        $notes = readData('notes');
        array_unshift($notes, ['id'=>genId(),'title'=>$action['title']??'','content'=>$content,'tag'=>$action['tag']??'Личное','created'=>todayDate()]);
        writeData('notes', $notes);
        $resp = "📝 Заметка сохранена";
        if ($action['title']??'') $resp .= ": <b>" . htmlspecialchars($action['title']) . "</b>";
        sendMsg($chatId, $resp, mainMenu());
        return;
    }

    // ---- TRACKER ----
    if ($type === 'add_tracker') {
        $tracker = readData('tracker');
        $entry = ['id'=>genId(),'date'=>todayDate(),'income'=>$action['income']??'','mood'=>$action['mood']??5,'sleep'=>$action['sleep']??'','sport'=>$action['sport']??'','note'=>''];
        $found = false;
        foreach ($tracker as &$e) {
            if (($e['date']??'') === todayDate()) { $e = array_merge($e, $entry); $found = true; break; }
        }
        if (!$found) array_unshift($tracker, $entry);
        writeData('tracker', $tracker);
        $mood = (int)($action['mood']??5);
        $moodEmoji = $mood >= 8 ? '😄' : ($mood >= 5 ? '😐' : '😔');
        $resp = "📈 <b>Трекер обновлён</b>\nНастроение: $moodEmoji $mood/10";
        if ($action['income']??'') $resp .= "\nДоход: " . fmtMoney($action['income']);
        if ($action['sleep']??'') $resp .= "\nСон: " . $action['sleep'] . "ч";
        if ($action['sport']??'') $resp .= "\nСпорт: " . $action['sport'] . "мин";
        sendMsg($chatId, $resp, mainMenu());
        return;
    }

    // ---- SUMMARY ----
    if ($type === 'summary') {
        sendMsg($chatId, buildSummary(), mainMenu());
        return;
    }

    // ---- UNKNOWN ----
    $msg = $action['message'] ?? 'Не понял команду. Попробуй переформулировать или используй меню.';
    sendMsg($chatId, "🤔 " . htmlspecialchars($msg), mainMenu());
}

function processVoice($chatId, $fileId) {
    // 1. Notify user
    sendMsg($chatId, "🎙 Слушаю...");

    // 2. Download audio
    $filePath = downloadVoice($fileId);
    if (!$filePath) {
        sendMsg($chatId, "⚠️ Не удалось загрузить аудио. Попробуй ещё раз.", mainMenu());
        return;
    }

    // 3. Transcribe
    if (!GROQ_API_KEY) {
        sendMsg($chatId, "⚠️ Groq API key не настроен. Добавь его в bot.php");
        @unlink($filePath);
        return;
    }

    $text = transcribeWithGroq($filePath);
    @unlink($filePath); // cleanup

    if (!$text) {
        sendMsg($chatId, "⚠️ Не удалось распознать речь. Говори чётче или ближе к микрофону.", mainMenu());
        return;
    }

    // 4. Show what heard
    sendMsg($chatId, "🗣 Услышал: <i>«" . htmlspecialchars($text) . "»</i>\n⏳ Обрабатываю...");

    // 5. Understand intent via Claude
    $action = understandWithClaude($text);

    // 6. Execute
    executeVoiceAction($chatId, $action);
}

// ============================================================
// MAIN — process webhook
// ============================================================

// Diag endpoint: открыть bot.php?diag=ВАШСЕКРЕТ — выдаст состояние окружения
if (($_GET['diag'] ?? '') === substr(BOT_TOKEN, -10)) {
    header('Content-Type: text/plain; charset=utf-8');
    echo "PHP: " . PHP_VERSION . "\n";
    echo "curl: " . (function_exists('curl_init') ? 'OK' : 'MISSING') . "\n";
    echo "json: " . (function_exists('json_decode') ? 'OK' : 'MISSING') . "\n";
    echo "DATA_DIR writable: " . (is_writable(DATA_DIR) ? 'YES' : 'NO') . "\n";
    echo "finances.json: " . (file_exists(DATA_DIR.'finances.json') ? filesize(DATA_DIR.'finances.json').' bytes' : 'MISSING') . "\n";
    echo "settings.json: " . (file_exists(DATA_DIR.'settings.json') ? filesize(DATA_DIR.'settings.json').' bytes' : 'MISSING') . "\n";
    echo "\n--- last 30 lines of _error.log ---\n";
    $logf = DATA_DIR . '_error.log';
    if (file_exists($logf)) {
        $lines = file($logf, FILE_IGNORE_NEW_LINES);
        echo implode("\n", array_slice($lines, -30));
    } else {
        echo "(нет файла — фатальных ошибок не было ИЛИ нет прав на запись)";
    }
    echo "\n\n--- live test buildFinList() ---\n";
    try {
        $out = buildFinList();
        echo "OK, " . strlen($out) . " bytes\n";
        echo "Last line: " . trim(preg_replace('/<[^>]+>/', '', substr($out, -200))) . "\n";
    } catch (\Throwable $e) {
        echo "FAIL: " . $e->getMessage() . "\n";
    }
    exit;
}

// Безопасное чтение тела запроса:
// под LiteSpeed file_get_contents('php://input') может зависать на GET/HEAD
// или при некорректных заголовках. Проверяем метод и Content-Length явно.
$rawInput = '';
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    $cl = (int)($_SERVER['CONTENT_LENGTH'] ?? 0);
    if ($cl > 0 && $cl < 10 * 1024 * 1024) {
        $rawInput = @file_get_contents('php://input', false, null, 0, $cl);
    }
}
$input = $rawInput !== '' ? json_decode($rawInput, true) : null;
if (!$input) exit;

$chatId = null;
$isAllowed = false;

if (isset($input['message'])) {
    $msg = $input['message'];
    $chatId = $msg['chat']['id'];
    $isAllowed = (string)$chatId === (string)ALLOWED_CHAT_ID;
    if (!$isAllowed) exit;

        // Voice message
    if (isset($msg['voice'])) {
        setState($chatId, null);
        processVoice($chatId, $msg['voice']['file_id']);
        exit;
    }
    // Video note (круглое видео)
    if (isset($msg['video_note'])) {
        setState($chatId, null);
        processVoice($chatId, $msg['video_note']['file_id']);
        exit;
    }
    $text = $msg['text'] ?? '';
    if (in_array($text, ['/start', '/menu'])) {
        setState($chatId, null);
        sendMsg($chatId, "👋 <b>Life CRM</b>\nВыбери раздел:", mainMenu());
    } else {
        processText($chatId, $text);
    }
} elseif (isset($input['callback_query'])) {
    $cb = $input['callback_query'];
    $chatId = $cb['message']['chat']['id'];
    $isAllowed = (string)$chatId === (string)ALLOWED_CHAT_ID;
    if (!$isAllowed) exit;
    processCallback($chatId, $cb['message']['message_id'], $cb['id'], $cb['data']);
}
