<?php
// STORAGE/TRADES — закрытые сделки + сигналы (запись/чтение/нормализация)
require_once __DIR__ . '/db.php';


// ---------------------------------------------------------------
// Запись закрытой сделки
// ---------------------------------------------------------------

/**
 * Низкоуровневый INSERT строки в trades_closed на переданном PDO-хэндле.
 * Единственное место, где описан этот INSERT — используется и одиночной
 * вставкой (storage_insert_closed_trade), и атомарным закрытием
 * (storage_close_trade_atomic), чтобы список колонок не расходился.
 */
function storage_insert_trade_row(PDO $db, array $trade): void
{
    $db->prepare('
        INSERT INTO trades_closed (
            open_time, close_time, symbol, side,
            entry_price, trader_entry_price, exit_price,
            size_usd, leverage,
            gross_pnl, entry_fee, exit_fee, funding, net_pnl, pnl_pct,
            entry_slippage_pct, exit_slippage_pct,
            trade_id, wallet_id, wallet_label, entry_reason, exit_reason
        ) VALUES (
            :open_time, :close_time, :symbol, :side,
            :entry_price, :trader_entry_price, :exit_price,
            :size_usd, :leverage,
            :gross_pnl, :entry_fee, :exit_fee, :funding, :net_pnl, :pnl_pct,
            :entry_slippage_pct, :exit_slippage_pct,
            :trade_id, :wallet_id, :wallet_label, :entry_reason, :exit_reason
        )
    ')->execute([
        ':open_time'          => $trade['open_time']          ?? '',
        ':close_time'         => $trade['close_time']         ?? '',
        ':symbol'             => $trade['symbol']             ?? '',
        ':side'               => $trade['side']               ?? '',
        ':entry_price'        => (float)  ($trade['entry_price']        ?? 0),
        ':trader_entry_price' => isset($trade['trader_entry_price']) && $trade['trader_entry_price'] !== ''
                                    ? (float) $trade['trader_entry_price'] : null,
        ':exit_price'         => (float)  ($trade['exit_price']         ?? 0),
        ':size_usd'           => (float)  ($trade['size_usd']           ?? 0),
        ':leverage'           => (int)    ($trade['leverage']           ?? 1),
        ':gross_pnl'          => (float)  ($trade['gross_pnl']          ?? 0),
        ':entry_fee'          => (float)  ($trade['entry_fee']          ?? 0),
        ':exit_fee'           => (float)  ($trade['exit_fee']           ?? 0),
        ':funding'            => (float)  ($trade['funding']            ?? 0),
        ':net_pnl'            => (float)  ($trade['net_pnl']            ?? 0),
        ':pnl_pct'            => (float)  ($trade['pnl_pct']            ?? 0),
        ':entry_slippage_pct' => isset($trade['entry_slippage_pct']) && $trade['entry_slippage_pct'] !== ''
                                    ? (float) $trade['entry_slippage_pct'] : null,
        ':exit_slippage_pct'  => isset($trade['exit_slippage_pct']) && $trade['exit_slippage_pct'] !== ''
                                    ? (float) $trade['exit_slippage_pct'] : null,
        ':trade_id'           => $trade['trade_id']           ?? '',
        ':wallet_id'          => $trade['wallet_id']          ?? '',
        ':wallet_label'       => $trade['wallet_label']       ?? '',
        ':entry_reason'       => $trade['entry_reason']       ?? 'normal',
        ':exit_reason'        => $trade['exit_reason']        ?? 'signal',
    ]);
}

/**
 * Сохраняет закрытую сделку в таблицу trades_closed.
 * Используется из position_tracker.php и pool_tracker.php.
 *
 * @param array $trade Полный набор полей:
 *   open_time, close_time, symbol, side,
 *   entry_price, trader_entry_price, exit_price,
 *   size_usd, leverage,
 *   gross_pnl, entry_fee, exit_fee, funding, net_pnl, pnl_pct,
 *   entry_slippage_pct, exit_slippage_pct,
 *   trade_id, wallet_id, wallet_label, entry_reason
 */
function storage_insert_closed_trade(array $trade): void
{
    storage_insert_trade_row(storage_db(), $trade);
}

// ---------------------------------------------------------------
// Запись сигнала
// ---------------------------------------------------------------

/**
 * Сохраняет сигнал в таблицу signals.
 * Используется из executor.php.
 */
function storage_insert_signal(array $signal, array $walletMeta, string $mode): void
{
    $db  = storage_db();
    $sql = '
        INSERT INTO signals (
            datetime, symbol, side, size, leverage,
            source_trade, trader_price, mode, wallet_id, wallet_label
        ) VALUES (
            :datetime, :symbol, :side, :size, :leverage,
            :source_trade, :trader_price, :mode, :wallet_id, :wallet_label
        )
    ';

    $stmt = $db->prepare($sql);
    $stmt->execute([
        ':datetime'     => date('Y-m-d H:i:s'),
        ':symbol'       => $signal['symbol']       ?? '',
        ':side'         => $signal['side']         ?? '',
        ':size'         => (float) ($signal['size']     ?? 0),
        ':leverage'     => (int)   ($signal['leverage'] ?? 1),
        ':source_trade' => $signal['source_trade'] ?? '',
        ':trader_price' => $signal['trader_price'] !== null ? (float) $signal['trader_price'] : null,
        ':mode'         => $mode,
        ':wallet_id'    => $walletMeta['id']       ?? '',
        ':wallet_label' => $walletMeta['label']    ?? '',
    ]);
}

/**
 * Удаляет сигналы старше $olderThanDays дней (retention-политика,
 * см. signals_retention_days в настройках). Возвращает число удалённых
 * строк для лога. 0 и меньше = очистка выключена, ничего не удаляем.
 *
 * Cutoff считается через date() в локальном времени PHP — в том же формате
 * и часовом поясе, в котором storage_insert_signal пишет колонку datetime
 * (SQLite-шный datetime('now') дал бы UTC и разъехался с записью).
 */
function storage_prune_old_signals(int $olderThanDays): int
{
    if ($olderThanDays <= 0) {
        return 0;
    }

    $cutoff = date('Y-m-d H:i:s', time() - $olderThanDays * 86400);
    $stmt   = storage_db()->prepare('DELETE FROM signals WHERE datetime < ?');
    $stmt->execute([$cutoff]);

    return $stmt->rowCount();
}

// ---------------------------------------------------------------
// Чтение (для data.php и analytics.php)
// ---------------------------------------------------------------

/**
 * Загружает закрытые сделки из БД.
 *
 * @param string|null $walletId  если задан — только сделки этого кошелька
 * @param int|null    $limit     максимальное число строк (null = все)
 * @param bool        $reverse   true = от новых к старым
 */
function storage_get_closed_trades(?string $walletId = null, ?int $limit = null, bool $reverse = false): array
{
    $db = storage_db();

    $order  = $reverse ? 'DESC' : 'ASC';
    $where  = '';
    $params = [];

    if ($walletId !== null) {
        $where = 'WHERE wallet_id = :wallet_id';
        $params[':wallet_id'] = $walletId;
    }

    $limitSql = $limit !== null ? 'LIMIT :limit' : '';
    $sql = "SELECT * FROM trades_closed $where ORDER BY close_time $order, id $order $limitSql";

    $stmt = $db->prepare($sql);

    foreach ($params as $k => $v) {
        $stmt->bindValue($k, $v);
    }
    if ($limit !== null) {
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
    }

    $stmt->execute();
    $rows = $stmt->fetchAll();

    return array_map('storage_normalize_trade_row', $rows);
}

/**
 * Загружает сигналы из БД.
 */
function storage_get_signals(?string $walletId = null, ?int $limit = null, bool $reverse = false): array
{
    $db = storage_db();

    $order  = $reverse ? 'DESC' : 'ASC';
    $where  = '';
    $params = [];

    if ($walletId !== null) {
        $where = 'WHERE wallet_id = :wallet_id';
        $params[':wallet_id'] = $walletId;
    }

    $limitSql = $limit !== null ? 'LIMIT :limit' : '';
    $sql = "SELECT * FROM signals $where ORDER BY datetime $order, id $order $limitSql";

    $stmt = $db->prepare($sql);

    foreach ($params as $k => $v) {
        $stmt->bindValue($k, $v);
    }
    if ($limit !== null) {
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
    }

    $stmt->execute();
    return $stmt->fetchAll();
}

// ---------------------------------------------------------------
// Нормализация строки из БД
// ---------------------------------------------------------------

/**
 * Приводит строку из SQLite к формату, который ждёт data.php
 * (NULL → пустая строка, числа уже float из PDO).
 */
function storage_normalize_trade_row(array $row): array
{
    foreach (['trader_entry_price', 'entry_slippage_pct', 'exit_slippage_pct'] as $field) {
        if ($row[$field] === null) {
            $row[$field] = '';
        }
    }

    if (empty($row['entry_reason'])) {
        $row['entry_reason'] = 'normal';
    }
    if (empty($row['exit_reason'])) {
        $row['exit_reason'] = 'signal';
    }

    return $row;
}
