<?php
// =============================================================
// DASHBOARD/LIB/POOLS_HANDLER — обработка POST страницы пулов
// =============================================================
// CSRF проверяется вызывающей страницей. Возвращает ['message','messageType'].
// Конфиг персистится через save_pools_config; страница перечитывает после.

require_once __DIR__ . '/../../config.php';
require_once __DIR__ . '/../../pool_tracker.php';
require_once __DIR__ . '/../../storage.php';
require_once __DIR__ . '/forms.php';

function dashboard_handle_pools_post(array $post, array $pools): array
{
    $action = $post['action'] ?? '';
    $message = '';
    $messageType = '';

    // --- Добавить пул ---
    if ($action === 'add_pool') {
        $label          = trim($post['pool_label'] ?? '');
        $virtualBalance = max(0, (float) ($post['virtual_balance'] ?? 1000));
        $leverage       = max(1, (int) ($post['leverage'] ?? 3));
        $feePct         = max(0, (float) ($post['fee_pct_per_side'] ?? 0.035));

        $newId = generate_pool_id($pools);
        $pools[] = [
            'id'               => $newId,
            'label'            => $label !== '' ? $label : $newId,
            'enabled'          => true,
            'virtual_balance'  => $virtualBalance,
            'leverage'         => $leverage,
            'fee_pct_per_side' => $feePct,
            // Лимиты объёма позиции (нотионал) на уровне пула; трейдер может
            // переопределить своими. 0 = выкл.
            'position_min_usd' => max(0, (float) ($post['pool_position_min_usd'] ?? 0)),
            'position_max_usd' => max(0, (float) ($post['pool_position_max_usd'] ?? 0)),
            'partial_close_mode' => (($post['partial_close_mode'] ?? 'off') === 'on') ? 'on' : 'off',
            // Прямая торговля пула на бирже: webhook (по умолчанию) | direct (live).
            'execution_mode'   => (($post['execution_mode'] ?? 'webhook') === 'direct') ? 'direct' : 'webhook',
            'hl_margin_mode'   => (($post['hl_margin_mode'] ?? 'isolated') === 'cross') ? 'cross' : 'isolated',
            'hl_subaccount'    => preg_match('/^0x[0-9a-fA-F]{40}$/', trim((string) ($post['hl_subaccount'] ?? ''))) ? strtolower(trim($post['hl_subaccount'])) : '',
            'traders'          => [],
        ];

        if (save_pools_config(['pools' => $pools])) {
            $initBalance = fresh_pool_balance($virtualBalance);
            storage_save_balance('pool', $newId, $initBalance);
            storage_save_positions('pool', $newId, []);
            $message = 'Пул "' . ($label !== '' ? $label : $newId) . '" создан';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
    }

    // --- Обновить пул (основные настройки; вебхуки — ОТДЕЛЬНЫЙ action ниже) ---
    if ($action === 'update_pool') {
        $poolId = $post['pool_id'] ?? '';
        foreach ($pools as &$p) {
            if ($p['id'] !== $poolId) continue;
            $p['label']            = trim($post['pool_label'] ?? $p['label']);
            $p['enabled']          = !empty($post['pool_enabled']);
            $p['virtual_balance']  = max(0, (float) ($post['virtual_balance'] ?? $p['virtual_balance']));
            $p['leverage']         = max(1, (int) ($post['leverage'] ?? $p['leverage']));
            $p['fee_pct_per_side'] = max(0, (float) ($post['fee_pct_per_side'] ?? $p['fee_pct_per_side']));
            $p['position_min_usd'] = max(0, (float) ($post['pool_position_min_usd'] ?? $p['position_min_usd'] ?? 0));
            $p['position_max_usd'] = max(0, (float) ($post['pool_position_max_usd'] ?? $p['position_max_usd'] ?? 0));
            $p['partial_close_mode'] = (($post['partial_close_mode'] ?? ($p['partial_close_mode'] ?? 'off')) === 'on') ? 'on' : 'off';
            $p['execution_mode']   = (($post['execution_mode'] ?? ($p['execution_mode'] ?? 'webhook')) === 'direct') ? 'direct' : 'webhook';
            $p['hl_margin_mode']   = (($post['hl_margin_mode'] ?? ($p['hl_margin_mode'] ?? 'isolated')) === 'cross') ? 'cross' : 'isolated';
            // hl_subaccount меняем ТОЛЬКО когда поле пришло в POST: отсутствие поля
            // (запрос из другой формы) не должно молча сбрасывать direct-пул на
            // мастер-аккаунт — реальные ордера ушли бы не на тот счёт, а лизы
            // остались бы под адресом старого субаккаунта.
            if (array_key_exists('hl_subaccount', $post)) {
                $subAcc = trim((string) $post['hl_subaccount']);
                $p['hl_subaccount'] = preg_match('/^0x[0-9a-fA-F]{40}$/', $subAcc) ? strtolower($subAcc) : '';
            }
            break;
        }
        unset($p);

        if (save_pools_config(['pools' => $pools])) {
            $message = 'Пул обновлён';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
    }

    // --- Обновить вебхуки пула ---
    // Отдельный action: раньше обе формы (настройки и вебхуки) слали update_pool,
    // и сохранение настроек затирало webhooks пустым списком (в форме настроек
    // нет полей pool_wh_*) — пул молча переставал слать сигналы приёмникам.
    if ($action === 'update_pool_webhooks') {
        $poolId = $post['pool_id'] ?? '';
        foreach ($pools as &$p) {
            if ($p['id'] !== $poolId) continue;
            $p['webhooks'] = parse_webhooks_from_post($post, 'pool_wh_');
            break;
        }
        unset($p);

        if (save_pools_config(['pools' => $pools])) {
            $message = 'Webhooks пула сохранены';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
    }

    // --- Удалить пул ---
    if ($action === 'delete_pool') {
        $poolId = $post['pool_id'] ?? '';
        $poolLabel = array_column($pools, 'label', 'id')[$poolId] ?? $poolId;
        $pools = array_values(array_filter($pools, fn($p) => $p['id'] !== $poolId));

        if (save_pools_config(['pools' => $pools])) {
            // Сворачиваем direct-след пула (реальные ордера/позиции/лизы), затем
            // чистим состояние пула (баланс, позиции, watching) и engine-state
            // всех его трейдеров. История сделок в trades_closed остаётся.
            hl_teardown_entity('pool', $poolId, $poolLabel);
            storage_delete_entity('pool', $poolId);
            storage_delete_pool_traders_state($poolId);
            $message = 'Пул удалён';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
    }

    // --- Сбросить state пула ---
    if ($action === 'reset_pool') {
        $poolId = $post['pool_id'] ?? '';
        $ok = true;
        foreach ($pools as $p) {
            if ($p['id'] !== $poolId) continue;
            $initBalance = fresh_pool_balance((float) $p['virtual_balance']);
            try {
                // Свернуть direct-след пула: сброс = чистый лист, реальная
                // позиция/сетка/лизы прежней жизни не должны пережить бумагу.
                hl_teardown_entity('pool', $poolId, $p['label'] ?? $poolId);

                storage_save_balance('pool', $poolId, $initBalance);
                storage_save_positions('pool', $poolId, []);
                storage_save_watching('pool', $poolId, []);
                // Сбрасываем watermark всех трейдеров пула
                foreach ($p['traders'] ?? [] as $t) {
                    $stateKey = $poolId . ':' . $t['id'];
                    storage_save_state('pool_trader', $stateKey, [
                        'trades'       => [],
                        'cooldown'     => [],
                        'last_seen_ts' => null,
                    ]);
                }
                $ok = true;
            } catch (Throwable $e) {
                bot_log('reset_pool error: ' . $e->getMessage());
                $ok = false;
            }
            break;
        }
        $message = $ok ? 'State пула сброшен' : 'Ошибка при сбросе';
        $messageType = $ok ? 'success' : 'error';
    }

    // --- Добавить трейдера в пул ---
    if ($action === 'add_trader') {
        $poolId  = $post['pool_id'] ?? '';
        $address = trim($post['trader_address'] ?? '');
        $label   = trim($post['trader_label'] ?? '');

        if (!preg_match('/^0x[a-fA-F0-9]{40}$/', $address)) {
            $message = 'Некорректный адрес кошелька';
            $messageType = 'error';
        } else {
            $newAllocationPct = max(0, min(100, (float) ($post['allocation_pct'] ?? 20)));

            // Проверяем что суммарная аллокация не превысит 100%
            $poolForCheck = null;
            foreach ($pools as $p) {
                if ($p['id'] === $poolId) { $poolForCheck = $p; break; }
            }
            $currentTotal = array_sum(array_column($poolForCheck['traders'] ?? [], 'allocation_pct'));
            if ($currentTotal + $newAllocationPct > 100) {
                $message = sprintf(
                    'Аллокация превысит 100%%. Уже занято: %.1f%%, запрошено: %.1f%%, доступно: %.1f%%',
                    $currentTotal, $newAllocationPct, max(0, 100 - $currentTotal)
                );
                $messageType = 'error';
            } else {
            foreach ($pools as &$p) {
                if ($p['id'] !== $poolId) continue;
                $newTid = generate_trader_id($p['traders'] ?? []);
                $p['traders'][] = [
                    'id'               => $newTid,
                    'label'            => $label !== '' ? $label : $newTid,
                    'address'          => $address,
                    'enabled'          => true,
                    'allocation_pct'   => max(0, min(100, (float) ($post['allocation_pct'] ?? 20))),
                    'sizing_mode'      => in_array($post['sizing_mode'] ?? 'percent_balance', ['fixed', 'percent_balance'], true)
                        ? ($post['sizing_mode'] ?? 'percent_balance') : 'percent_balance',
                    'sizing_fixed_usd' => ($post['sizing_fixed_usd'] ?? '') !== '' ? max(0, (float) $post['sizing_fixed_usd']) : null,
                    'sizing_percent'   => max(0, min(100, (float) ($post['sizing_percent'] ?? 100))),
                    // Лимиты объёма позиции (нотионал); пусто = наследовать лимиты пула.
                    'position_min_usd' => ($post['position_min_usd'] ?? '') !== '' ? max(0, (float) $post['position_min_usd']) : null,
                    'position_max_usd' => ($post['position_max_usd'] ?? '') !== '' ? max(0, (float) $post['position_max_usd']) : null,
                    // Персональное плечо трейдера; пусто = наследовать плечо пула.
                    'leverage'         => ($post['trader_leverage'] ?? '') !== '' ? max(1, (int) $post['trader_leverage']) : null,
                    'allowed_symbols'  => [],
                    'cooldown_seconds' => max(0, (int) ($post['trader_cooldown'] ?? 30)),
                ];
                break;
            }
            unset($p);

            if (save_pools_config(['pools' => $pools])) {
                $message = 'Трейдер добавлен в пул';
                $messageType = 'success';
            } else {
                $message = 'Ошибка сохранения';
                $messageType = 'error';
            }
            } // end else (allocation <= 100%)
        } // end else (address valid)
    }

    // --- Обновить трейдера в пуле ---
    if ($action === 'update_trader') {
        $poolId   = $post['pool_id'] ?? '';
        $traderId = $post['trader_id'] ?? '';
        $newAlloc = max(0, min(100, (float) ($post['allocation_pct'] ?? 0)));

        // Адрес валидируем так же, как при добавлении (add_trader): опечатка при
        // редактировании молча ломала бы сбор fills этого трейдера.
        $newAddress = array_key_exists('trader_address', $post) ? trim((string) $post['trader_address']) : null;
        if ($newAddress !== null && !preg_match('/^0x[a-fA-F0-9]{40}$/', $newAddress)) {
            return ['message' => 'Некорректный адрес кошелька (ожидается формат 0x... из 42 символов)', 'messageType' => 'error'];
        }

        // Проверяем сумму аллокаций с учётом обновляемого трейдера
        $allocationError = false;
        foreach ($pools as $p) {
            if ($p['id'] !== $poolId) continue;
            $othersTotal = 0;
            foreach ($p['traders'] as $t) {
                if ($t['id'] !== $traderId) $othersTotal += (float) $t['allocation_pct'];
            }
            if ($othersTotal + $newAlloc > 100) {
                $message = sprintf(
                    'Аллокация превысит 100%%. Остальные трейдеры: %.1f%%, новое значение: %.1f%%, доступно: %.1f%%',
                    $othersTotal, $newAlloc, max(0, 100 - $othersTotal)
                );
                $messageType = 'error';
                $allocationError = true;
            }
            break;
        }

        if (!$allocationError) {
        foreach ($pools as &$p) {
            if ($p['id'] !== $poolId) continue;
            foreach ($p['traders'] as &$t) {
                if ($t['id'] !== $traderId) continue;
                $t['label']            = trim($post['trader_label'] ?? $t['label']);
                $t['address']          = $newAddress ?? $t['address'];
                $t['enabled']          = !empty($post['trader_enabled']);
                $t['allocation_pct']   = max(0, min(100, (float) ($post['allocation_pct'] ?? $t['allocation_pct'])));
                // ?? и в true-ветке: без него отсутствие поля в POST (запрос из
                // другой формы) читало бы несуществующий ключ и затирало режим в null.
                $newSizing = $post['sizing_mode'] ?? $t['sizing_mode'];
                $t['sizing_mode']      = in_array($newSizing, ['fixed', 'percent_balance'], true)
                    ? $newSizing : $t['sizing_mode'];
                $t['sizing_fixed_usd']            = ($post['sizing_fixed_usd'] ?? '') !== '' ? max(0, (float) $post['sizing_fixed_usd']) : null;
                $t['sizing_percent']              = max(0, min(100, (float) ($post['sizing_percent'] ?? $t['sizing_percent'])));
                // Лимиты объёма позиции (нотионал); пусто = наследовать лимиты пула.
                $t['position_min_usd']            = ($post['position_min_usd'] ?? '') !== '' ? max(0, (float) $post['position_min_usd']) : null;
                $t['position_max_usd']            = ($post['position_max_usd'] ?? '') !== '' ? max(0, (float) $post['position_max_usd']) : null;
                // Персональное плечо: пусто = наследовать плечо пула (null).
                $t['leverage']                    = ($post['trader_leverage'] ?? '') !== '' ? max(1, (int) $post['trader_leverage']) : null;
                $t['cooldown_seconds']            = max(0, (int) ($post['trader_cooldown'] ?? $t['cooldown_seconds']));
                $t['copy_after_drawdown_enabled'] = !empty($post['copy_after_drawdown_enabled']) && $post['copy_after_drawdown_enabled'] === '1';
                $t['copy_after_drawdown_pct']     = max(0, min(100, (float) ($post['copy_after_drawdown_pct'] ?? $t['copy_after_drawdown_pct'] ?? 5)));
                $t['copy_after_drawdown_ttl_hours'] = max(0, (float) ($post['copy_after_drawdown_ttl_hours'] ?? $t['copy_after_drawdown_ttl_hours'] ?? 0));
                // Прямая торговля (задействуется, когда у пула execution_mode=direct):
                // сетка/SL/TP трейдера хранятся напрямую (без override/null, как у кошелька).
                $t['stop_loss']    = parse_protective_config($post, '', 'sl');
                $t['take_profit']  = parse_protective_config($post, '', 'tp');
                $t['grid']         = parse_grid_config($post, '');
                break 2;
            }
            unset($t);
        }
        unset($p);

        if (save_pools_config(['pools' => $pools])) {
            $message = 'Трейдер обновлён';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
        } // end if (!$allocationError)
    }

    // --- Удалить трейдера из пула ---
    if ($action === 'remove_trader') {
        $poolId   = $post['pool_id'] ?? '';
        $traderId = $post['trader_id'] ?? '';
        foreach ($pools as &$p) {
            if ($p['id'] !== $poolId) continue;
            $p['traders'] = array_values(array_filter($p['traders'], fn($t) => $t['id'] !== $traderId));
            break;
        }
        unset($p);

        if (save_pools_config(['pools' => $pools])) {
            // Engine-state и seen_fills чистим ТОЛЬКО после успешной записи конфига:
            // при сбое сохранения трейдер остаётся в пуле, и его watermark не должен
            // пропасть (иначе следующий цикл переинициализировал бы его с нуля).
            storage_delete_entity('pool_trader', $poolId . ':' . $traderId);
            $message = 'Трейдер удалён из пула';
            $messageType = 'success';
        } else {
            $message = 'Ошибка сохранения';
            $messageType = 'error';
        }
    }

    return ['message' => $message, 'messageType' => $messageType];
}
