<?php
declare(strict_types=1);

namespace Celena\Module\Users;

use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Security\Auth;
use Celena\Core\Security\Csrf;
use Celena\Core\Security\RateLimiter;
use Celena\Core\Template\Engine;
use Celena\Module\Settings\Options;

/**
 * Логин/логаут админки.
 */
final class AuthController
{
    public function __construct(
        private readonly Engine $engine,
        private readonly Auth $auth,
        private readonly RateLimiter $limiter,
        private readonly Options $options,
    ) {
    }

    public function showLogin(Request $request): Response
    {
        if ($this->auth->check()) {
            return Response::redirect('/admin');
        }
        return $this->renderLogin([]);
    }

    public function login(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            // Форма перерисуется со свежим токеном (сессия уже актуальна), поэтому
            // достаточно просто повторить вход — без технических подробностей про CSRF.
            // Формулировка — как на фронтовом логине (CustomerAuthController).
            return $this->renderLogin(['error' => 'Сессия устарела, попробуйте ещё раз.']);
        }
        $email = strtolower(trim((string) $request->input('email', '')));
        $pass = (string) $request->input('password', '');
        $remember = (bool) $request->input('remember', false);

        // Двухуровневый rate-limit:
        //   1) IP+email — 5/5 минут — ловит брутфорс конкретного аккаунта.
        //   2) только IP — 30/15 минут — ловит распыление по множеству email с одного IP.
        $perAccountKey = 'login:' . $request->ip() . ':' . $email;
        $perIpKey      = 'login-ip:' . $request->ip();
        if (!$this->limiter->attempt($perIpKey, 30, 900)) {
            return $this->renderLogin(['error' => 'Слишком много попыток с этого IP. Подождите ' . $this->limiter->retryAfter($perIpKey) . ' сек.']);
        }
        if (!$this->limiter->attempt($perAccountKey, 5, 300)) {
            return $this->renderLogin(['error' => 'Слишком много попыток. Подождите ' . $this->limiter->retryAfter($perAccountKey) . ' сек.']);
        }
        if (!$this->auth->attempt($email, $pass, $remember)) {
            return $this->renderLogin(['error' => 'Неверный email или пароль.']);
        }
        $this->limiter->reset($perAccountKey);
        $this->limiter->reset($perIpKey);
        return Response::redirect('/admin');
    }

    public function logout(Request $request): Response
    {
        // Logout — POST с CSRF, чтобы исключить разлогин по `<img src="/admin/logout">`.
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return Response::redirect('/admin');
        }
        $this->auth->logout();
        return Response::redirect('/admin/login');
    }

    private function renderLogin(array $extra): Response
    {
        $this->engine->setTheme('admin');
        $vars = array_merge([
            'site' => ['name' => (string) $this->options->get('site_name', 'Celena CMS')],
            'page' => ['title' => 'Вход в панель'],
            'asset_v' => $this->assetVersion(),
        ], $extra);
        return Response::html($this->engine->render('login', $vars));
    }

    /**
     * Версия статики для cache-busting (?v=…) на странице логина: она рендерится
     * вне AdminController, поэтому считаем mtime подключаемых здесь файлов сами —
     * иначе браузер держит старый admin.css после правок стилей.
     */
    private function assetVersion(): string
    {
        $base = \Celena\Core\Kernel::rootPath() . '/public/assets';
        $max = 0;
        foreach (['/runtime/celena.css', '/admin/admin.css', '/runtime/celena.js'] as $rel) {
            $t = @filemtime($base . $rel);
            if ($t !== false && $t > $max) {
                $max = $t;
            }
        }
        return $max > 0 ? (string) $max : '1';
    }
}
