<?php
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/auth.php';

$error = '';

// --- Защита от брутфорса ---
// Счётчик неудачных попыток хранится в state/login_throttle.json по IP клиента
// (а НЕ в сессии): счётчик-в-сессии тривиально обходился сбросом cookie —
// каждый запрос без cookie начинал с нуля, lockout не наступал. Теперь бакет
// привязан к REMOTE_ADDR (реальный пир; X-Forwarded-For НЕ используем — его
// контролирует клиент). После LOGIN_MAX_ATTEMPTS неверных паролей доступ с
// этого IP блокируется на LOGIN_LOCKOUT_SEC секунд.
const LOGIN_MAX_ATTEMPTS = 5;
const LOGIN_LOCKOUT_SEC  = 300; // 5 минут

function login_throttle_path(): string
{
    return STATE_DIR . '/login_throttle.json';
}

/**
 * IP клиента для бакета троттлинга. Берём именно REMOTE_ADDR (реальный пир):
 * X-Forwarded-For задаётся клиентом и позволил бы обойти лимит или отравить
 * чужой бакет. За обратным прокси все запросы разделят IP прокси — это
 * безопасный (в сторону блокировки) отказ, приемлемый для панели.
 */
function login_client_ip(): string
{
    $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    return $ip !== '' ? $ip : 'unknown';
}

/** Текущий бакет IP без блокировки (для read-only проверки). */
function login_throttle_entry(string $ip): array
{
    $path = login_throttle_path();
    if (!is_file($path)) {
        return ['attempts' => 0, 'locked_until' => 0, 'updated_at' => 0];
    }
    $data = json_decode((string) @file_get_contents($path), true);
    return is_array($data[$ip] ?? null)
        ? $data[$ip]
        : ['attempts' => 0, 'locked_until' => 0, 'updated_at' => 0];
}

/**
 * Атомарно (flock) читает-меняет-пишет бакет IP. $mutator получает текущий
 * бакет и возвращает новый (или null — удалить). Заодно прунит протухшие
 * записи. Возвращает новый бакет. При невозможности открыть файл деградирует
 * без персиста (лучше пустить, чем заблокировать легитимный вход).
 */
function login_throttle_update(string $ip, callable $mutator): array
{
    $path = login_throttle_path();
    $dir  = dirname($path);
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }
    $fp = @fopen($path, 'c+');
    if (!$fp) {
        return $mutator(['attempts' => 0, 'locked_until' => 0, 'updated_at' => 0]);
    }
    try {
        flock($fp, LOCK_EX);
        $raw  = stream_get_contents($fp);
        $data = json_decode((string) $raw, true);
        if (!is_array($data)) {
            $data = [];
        }
        $now = time();
        // Прунинг: снятые блокировки и давно не тронутые бакеты выкидываем.
        foreach ($data as $k => $e) {
            $lockedUntil = (int) ($e['locked_until'] ?? 0);
            $updatedAt   = (int) ($e['updated_at'] ?? 0);
            if ($lockedUntil < $now && ($now - $updatedAt) > LOGIN_LOCKOUT_SEC) {
                unset($data[$k]);
            }
        }
        $entry = is_array($data[$ip] ?? null)
            ? $data[$ip]
            : ['attempts' => 0, 'locked_until' => 0, 'updated_at' => 0];
        $entry = $mutator($entry);
        if ($entry === null) {
            unset($data[$ip]);
            $entry = ['attempts' => 0, 'locked_until' => 0, 'updated_at' => 0];
        } else {
            $data[$ip] = $entry;
        }
        ftruncate($fp, 0);
        rewind($fp);
        fwrite($fp, json_encode($data));
        fflush($fp);
        return $entry;
    } finally {
        flock($fp, LOCK_UN);
        fclose($fp);
    }
}

function login_is_locked(string $ip): bool
{
    return time() < (int) (login_throttle_entry($ip)['locked_until'] ?? 0);
}

function login_remaining_seconds(string $ip): int
{
    return max(0, (int) (login_throttle_entry($ip)['locked_until'] ?? 0) - time());
}

$clientIp = login_client_ip();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $password = $_POST['password'] ?? '';

    if (login_is_locked($clientIp)) {
        $error = 'Слишком много попыток. Подождите ' . login_remaining_seconds($clientIp) . ' сек.';
    } elseif (hash_equals(DASHBOARD_PASSWORD, $password)) {
        // Успешный вход — сбрасываем бакет IP и переиздаём session ID
        login_throttle_update($clientIp, fn($e) => null);
        session_regenerate_id(true);
        $_SESSION['authenticated'] = true;
        header('Location: index.php');
        exit;
    } else {
        // Неверный пароль — задержка + инкремент бакета IP
        sleep(1);
        $entry = login_throttle_update($clientIp, function (array $e) {
            $e['attempts']    = (int) ($e['attempts'] ?? 0) + 1;
            $e['updated_at']  = time();
            if ($e['attempts'] >= LOGIN_MAX_ATTEMPTS) {
                $e['locked_until'] = time() + LOGIN_LOCKOUT_SEC;
            }
            return $e;
        });
        if ((int) $entry['attempts'] >= LOGIN_MAX_ATTEMPTS) {
            $error = 'Слишком много попыток. Доступ заблокирован на ' . LOGIN_LOCKOUT_SEC . ' сек.';
        } else {
            $remaining = LOGIN_MAX_ATTEMPTS - (int) $entry['attempts'];
            $error = 'Неверный пароль. Осталось попыток: ' . $remaining;
        }
    }
}

// Если уже залогинен — сразу на dashboard
if (!empty($_SESSION['authenticated'])) {
    header('Location: index.php');
    exit;
}
?>

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#0B0D12">
<title>Вход — Hyper Analytics</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@500;600;700;800&family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/app.css">
</head>
<body>
<div class="login-wrap">
    <div class="card login-card">
        <div class="sb-logo">H</div>
        <div style="text-align:center; margin-bottom:22px">
            <div style="font-family:var(--font-ui); font-size:24px; font-weight:800; letter-spacing:-.02em">Hyper Analytics</div>
            <div class="dim" style="font-size:12.5px; margin-top:2px">Терминал копитрейдинга</div>
        </div>
        <?php if ($error): ?>
            <div class="message error" style="text-align:center"><?= htmlspecialchars($error) ?></div>
        <?php endif; ?>
        <form method="POST">
            <div class="field"><label>Пароль</label><input type="password" name="password" placeholder="••••••••" autofocus required></div>
            <button class="btn" type="submit" style="width:100%; justify-content:center">Войти</button>
        </form>
    </div>
</div>
</body>
</html>
