<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\InviteCode;
use App\Models\OperationLog;
use App\Models\User;
use App\Support\ApiResponse;
use hg\apidoc\annotation as Apidoc;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
use Tymon\JWTAuth\Facades\JWTAuth;

#[Apidoc\Group('认证')]
#[Apidoc\Title('认证接口')]
final class AuthController extends Controller
{
    #[Apidoc\Title('邀请码注册')]
    #[Apidoc\Url('/auth/register')]
    #[Apidoc\Method('POST')]
    public function register(Request $request): JsonResponse
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:50'],
            'email' => ['required', 'email', 'max:120', 'unique:users,email'],
            'password' => ['required', 'string', 'min:6', 'confirmed'],
            'invite_code' => ['required', 'string'],
        ]);

        $invite = InviteCode::query()->where('code', $data['invite_code'])->lockForUpdate()->first();
        if (! $invite || ! $invite->available()) {
            throw ValidationException::withMessages(['invite_code' => '邀请码无效']);
        }
        if ($invite->assigned_name !== null && trim($data['name']) !== $invite->assigned_name) {
            throw ValidationException::withMessages(['name' => '姓名与邀请码指定使用人不一致']);
        }

        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'role' => $invite->role,
            'is_active' => true,
            'password' => Hash::make($data['password']),
        ]);

        $invite->increment('used_count');

        return ApiResponse::success($this->tokenPayload($user), '注册成功');
    }

    #[Apidoc\Title('验证码')]
    #[Apidoc\Url('/auth/captcha')]
    #[Apidoc\Method('GET')]
    public function captcha(Request $request): JsonResponse
    {
        $code = (string) random_int(1000, 9999);
        $request->session()->put('captcha', $code);

        return ApiResponse::success([
            'captcha' => $code,
            'expires_in' => 300,
        ], '验证码已生成');
    }

    #[Apidoc\Title('登录')]
    #[Apidoc\Url('/auth/login')]
    #[Apidoc\Method('POST')]
    public function login(Request $request): JsonResponse
    {
        $data = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required', 'string'],
        ]);

        $user = User::query()->where('email', $data['email'])->first();

        if (! $user || ! Hash::check($data['password'], $user->password)) {
            $user?->update([
                'failed_login_count' => $user->failed_login_count + 1,
                'last_failed_login_at' => now(),
            ]);

            return ApiResponse::error('账号或密码错误', 422, 422);
        }

        if (! $user->is_active) {
            return ApiResponse::error('账号已被禁用', 403, 403);
        }

        $user->update(['failed_login_count' => 0, 'last_login_at' => now()]);

        OperationLog::create([
            'user_id' => $user->id,
            'action' => 'auth.login',
            'ip' => $request->ip(),
        ]);

        return ApiResponse::success($this->tokenPayload($user), '登录成功');
    }

    #[Apidoc\Title('刷新Token')]
    #[Apidoc\Url('/auth/refresh')]
    #[Apidoc\Method('POST')]
    public function refresh(): JsonResponse
    {
        return ApiResponse::success([
            'token' => JWTAuth::refresh(JWTAuth::getToken()),
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60,
        ]);
    }

    #[Apidoc\Title('当前用户')]
    #[Apidoc\Url('/auth/me')]
    #[Apidoc\Method('GET')]
    #[Apidoc\RouteMiddleware(['jwt.auth'])]
    public function me(Request $request): JsonResponse
    {
        return ApiResponse::success($request->user()->loadMissing('permissions'));
    }

    #[Apidoc\Title('退出登录')]
    #[Apidoc\Url('/auth/logout')]
    #[Apidoc\Method('POST')]
    #[Apidoc\RouteMiddleware(['jwt.auth'])]
    public function logout(): JsonResponse
    {
        JWTAuth::invalidate(JWTAuth::getToken());

        return ApiResponse::success(null, '已退出');
    }

    private function tokenPayload(User $user): array
    {
        return [
            'token' => JWTAuth::fromUser($user),
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60,
            'user' => $user,
        ];
    }
}
