<?php
declare(strict_types=1);

namespace Celena\Module\Account;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Plugin\Hook;
use Celena\Core\Security\Auth;
use Celena\Core\Security\Csrf;
use Celena\Core\Security\Filter;
use Celena\Core\Security\Password;
use Celena\Core\Security\RateLimiter;
use Celena\Core\Template\Engine;
use Celena\Module\Settings\Options;

/**
 * Аутентификация посетителя для личного кабинета (/account).
 * Покупатель — строка celena_users с ролью `customer`. Переиспользуем общий
 * Auth (сессия, remember-me) и rate-limit, как админский AuthController.
 *
 * После успешного входа/регистрации вызываем экшен `account.authenticated`,
 * на который плагины могут подписаться (магазин привязывает гостевые заказы
 * по email к user_id) — без правок ядра.
 */
final class CustomerAuthController extends AccountBaseController
{
    public function __construct(
        Engine $engine,
        Auth $auth,
        Options $options,
        Connection $conn,
        private readonly RateLimiter $limiter,
    ) {
        parent::__construct($engine, $auth, $options, $conn);
    }

    public function showLogin(Request $request): Response
    {
        if ($this->auth->check()) {
            return Response::redirect('/account');
        }
        return $this->render('account/login', [
            'next' => $this->safeNext((string) $request->query('next', '')),
        ], 'Вход');
    }

    public function login(Request $request): Response
    {
        $next = $this->safeNext((string) $request->input('next', ''));
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->loginError('Сессия устарела, попробуйте ещё раз.', '', $next);
        }
        $email = strtolower(trim((string) $request->input('email', '')));
        $pass = (string) $request->input('password', '');
        $remember = Filter::bool($request->input('remember'));

        $perAccountKey = 'acc-login:' . $request->ip() . ':' . $email;
        $perIpKey = 'acc-login-ip:' . $request->ip();
        if (!$this->limiter->attempt($perIpKey, 30, 900)) {
            return $this->loginError('Слишком много попыток с этого IP. Подождите ' . $this->limiter->retryAfter($perIpKey) . ' сек.', $email, $next);
        }
        if (!$this->limiter->attempt($perAccountKey, 5, 300)) {
            return $this->loginError('Слишком много попыток. Подождите ' . $this->limiter->retryAfter($perAccountKey) . ' сек.', $email, $next);
        }
        if (!$this->auth->attempt($email, $pass, $remember)) {
            return $this->loginError('Неверный email или пароль.', $email, $next);
        }
        $this->limiter->reset($perAccountKey);
        $this->limiter->reset($perIpKey);
        Hook::action('account.authenticated', $this->auth->user());
        return Response::redirect($next !== '' ? $next : '/account');
    }

    public function showRegister(Request $request): Response
    {
        if ($this->auth->check()) {
            return Response::redirect('/account');
        }
        return $this->render('account/register', [], 'Регистрация');
    }

    public function register(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->registerError('Сессия устарела, попробуйте ещё раз.', [], $request);
        }
        $key = 'acc-register-ip:' . $request->ip();
        if (!$this->limiter->attempt($key, 10, 900)) {
            return $this->registerError('Слишком много попыток. Подождите ' . $this->limiter->retryAfter($key) . ' сек.', [], $request);
        }
        $name = Filter::string($request->input('name'), 191);
        $email = Filter::email($request->input('email'));
        $pass = (string) $request->input('password', '');
        $pass2 = (string) $request->input('password_confirm', '');

        if ($name === '') {
            return $this->registerError('Укажите имя.', $request->post, $request);
        }
        if ($email === null) {
            return $this->registerError('Укажите корректный email.', $request->post, $request);
        }
        if (strlen($pass) < 8) {
            return $this->registerError('Пароль должен быть не короче 8 символов.', $request->post, $request);
        }
        if ($pass !== $pass2) {
            return $this->registerError('Пароли не совпадают.', $request->post, $request);
        }
        $exists = $this->conn->builder('celena_users')->where('email', '=', strtolower($email))->first();
        if ($exists !== null) {
            return $this->registerError('Пользователь с таким email уже зарегистрирован. Войдите.', $request->post, $request);
        }

        $now = date('Y-m-d H:i:s');
        $id = (int) $this->conn->builder('celena_users')->insert([
            'email'         => strtolower($email),
            'password_hash' => Password::hash($pass),
            'name'          => $name,
            'role'          => 'customer',
            'is_active'     => true,
            'locale'        => (string) $this->options->get('default_locale', 'ru'),
            'created_at'    => $now,
            'updated_at'    => $now,
        ]);
        $this->limiter->reset($key);
        $this->auth->login($id, true);
        Hook::action('account.authenticated', $this->auth->user());
        return Response::redirect('/account');
    }

    public function logout(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return Response::redirect('/account');
        }
        $this->auth->logout();
        return Response::redirect('/');
    }

    private function loginError(string $error, string $email, string $next): Response
    {
        return $this->render('account/login', [
            'error' => $error,
            'old'   => ['email' => $email],
            'next'  => $next,
        ], 'Вход');
    }

    /** @param array<string, mixed> $post */
    private function registerError(string $error, array $post, Request $request): Response
    {
        return $this->render('account/register', [
            'error' => $error,
            'old'   => [
                'name'  => Filter::string($post['name'] ?? '', 191),
                'email' => (string) ($post['email'] ?? ''),
            ],
        ], 'Регистрация');
    }

    /** Разрешаем только внутренние относительные пути (защита от open redirect). */
    private function safeNext(string $next): string
    {
        if ($next === '' || !str_starts_with($next, '/') || str_starts_with($next, '//')) {
            return '';
        }
        return $next;
    }
}
