<?php
declare(strict_types=1);

/**
 * 登录 / 退出 / 找回密码，全部不要求登录态（覆盖 requiresLogin）
 */
class loginAction extends Action
{
    /** 内置超级管理员，写死在代码里，不入库；admin_id 用 0 这个真实表里不可能出现的值区分 */
    private const SUPER_USERNAME = 'test.admin';
    private const SUPER_PASSWORD = 'TEST@2026@2026';

    protected function requiresLogin(): bool
    {
        return false;
    }

    private function gridModel(): Users
    {
        return new Users();
    }

    /**
     * 登录页。已登录直接跳首页，不用再看一遍登录界面
     */
    public function index(): void
    {
        if (Auth::user() !== null || Auth::tryRememberLogin()) {
            header('Location: ' . HOST . '/index/index');
            exit;
        }

        View::set([
            'pageTitle'      => lg('登录'),
            // 第一次登录不用验证码，密码错误一次后才强制要求，直到下次登录成功
            'requireCaptcha' => !empty($_SESSION['requireCaptcha']),
        ]);
        View::put();
    }

    /**
     * 验证码图片，<img src> 直接指向这个地址
     */
    public function captcha(): void
    {
        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        echo Captcha::render();
    }

    /**
     * 登录表单提交：普通 form POST（不是 ajax），方便浏览器原生记住密码/自动填充
     * 成功跳首页，失败带 ?error= 跳回登录页
     */
    public function doLogin(): void
    {
        $ip = clientIp();
        $lockedMinutes = LoginThrottle::lockedMinutesLeft($ip);
        if ($lockedMinutes !== null) {
            $this->backToLogin('locked', ['minutes' => $lockedMinutes]);
        }

        $username = trim((string) ($this->request['username'] ?? ''));
        $password = (string) ($this->request['password'] ?? '');
        $captcha = $this->request['captcha'] ?? '';
        $remember = !empty($this->request['remember']);

        if (!empty($_SESSION['requireCaptcha']) && !Captcha::verify($captcha)) {
            $this->backToLogin('captcha');
        }

        if ($username === self::SUPER_USERNAME && $password === self::SUPER_PASSWORD) {
            LoginThrottle::recordSuccess($ip);
            $_SESSION['requireCaptcha'] = false;
            Auth::login(['admin_id' => 0, 'admin_username' => self::SUPER_USERNAME, 'admin_name' => self::SUPER_USERNAME], $remember);
            header('Location: ' . HOST . '/index/index');
            exit;
        }

        $admin = $this->gridModel()->findByUsername($username);
        if (!$admin || !password_verify($password, $admin['admin_password'])) {
            LoginThrottle::recordFailure($ip);
            $_SESSION['requireCaptcha'] = true;
            $this->backToLogin('credentials');
        }
        if (!$admin['admin_allow']) {
            $this->backToLogin('disabled');
        }

        LoginThrottle::recordSuccess($ip);
        $_SESSION['requireCaptcha'] = false;
        Auth::login($admin, $remember);

        if ($this->gridModel()->isPasswordStale($admin)) {
            header('Location: ' . HOST . '/profile/index?passwordExpired=1');
            exit;
        }

        header('Location: ' . HOST . '/index/index');
        exit;
    }

    private function backToLogin(string $reason, array $extra = []): void
    {
        $query = array_merge(['error' => $reason], $extra);
        header('Location: ' . HOST . '/login/index?' . http_build_query($query));
        exit;
    }

    /**
     * 退出登录
     */
    public function logout(): void
    {
        Auth::logout();
        header('Location: ' . HOST . '/login/index');
        exit;
    }

    /**
     * 找回密码第一步：提交用户名或邮箱，后端校验存在则发邮件，不存在也返回成功（不暴露用户是否存在）
     */
    public function forgotPassword(): void
    {
        $usernameOrEmail = trim((string) ($this->request['usernameOrEmail'] ?? ''));
        if ($usernameOrEmail === '') {
            $this->error(1, lg('请输入用户名或邮箱'));
        }

        $admin = $this->gridModel()->findByUsername($usernameOrEmail)
            ?? $this->gridModel()->findByUsernameOrEmail($usernameOrEmail);

        if ($admin && !empty($admin['admin_email'])) {
            $reset = new PasswordReset();
            $token = $reset->createForUser((int) $admin['admin_id']);
            $link = HOST . '/login/resetPassword?token=' . urlencode($token);
            $url = (!empty($_SERVER['HTTPS']) ? 'https://' : 'http://') . ($_SERVER['HTTP_HOST'] ?? '') . $link;

            Mailer::send(
                $admin['admin_email'],
                lg('重置密码'),
                '<p>' . lg('您正在重置密码，链接30分钟内有效') . '：</p><p><a href="' . htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a></p>'
            );
        }

        $this->success();
    }

    /**
     * 找回密码第二步：展示重置页面，校验链接里的 token 是否有效，把对应用户名传给前端展示
     */
    public function resetPassword(): void
    {
        $token = (string) ($this->request['token'] ?? '');
        [$selector, $validator] = array_pad(explode(':', $token, 2), 2, '');

        $admin = $selector !== '' && $validator !== ''
            ? (new PasswordReset())->findValidUser($selector, $validator)
            : null;

        View::set([
            'pageTitle' => lg('重置密码'),
            'token'     => $token,
            'valid'     => $admin !== null,
            'username'  => $admin['admin_username'] ?? '',
        ]);
        View::put();
    }

    /**
     * 找回密码第三步：提交新密码
     */
    public function doResetPassword(): void
    {
        $token = (string) ($this->request['token'] ?? '');
        $password = (string) ($this->request['password'] ?? '');
        $passwordConfirm = (string) ($this->request['passwordConfirm'] ?? '');

        [$selector, $validator] = array_pad(explode(':', $token, 2), 2, '');
        $resetModel = new PasswordReset();
        $admin = $selector !== '' && $validator !== '' ? $resetModel->findValidUser($selector, $validator) : null;

        if (!$admin) {
            $this->error(1, lg('链接已失效，请重新申请'));
        }
        if ($password !== $passwordConfirm) {
            $this->error(2, lg('两次输入的密码不一致'));
        }
        if (!PasswordPolicy::isValid($password)) {
            $this->error(3, lg('密码必须大于8位，且包含大写字母、小写字母、数字、特殊符号'));
        }

        $this->gridModel()->updatePasswordById((int) $admin['admin_id'], $password);
        $resetModel->markUsed($selector);

        $this->success();
    }
}
