<?php

declare(strict_types=1);

namespace App\Auth;

use App\Models\PersonalAccessToken;
use App\Models\User;
use Hyperf\Context\Context;
use Psr\Http\Message\ServerRequestInterface;

class BearerTokenResolver
{
    public const CONTEXT_KEY = '__bearer.access_token';

    public static function resolve(ServerRequestInterface $request): ?User
    {
        $plain = self::extract($request);
        if ($plain === null) {
            return null;
        }

        $secret = str_contains($plain, '|') ? explode('|', $plain, 2)[1] : $plain;
        $token = PersonalAccessToken::findByPlainToken($secret);

        if ($token === null || ! $token->isActive()) {
            return null;
        }

        $user = $token->tokenable()->first();
        if (! $user instanceof User || ! $user->active) {
            return null;
        }

        $ip = self::clientIp($request);
        $token->forceFill([
            'last_used_at' => now(),
            'last_used_ip' => $ip,
        ])->save();

        $user->withAccessToken($token);
        Context::set(self::CONTEXT_KEY, $token);

        return $user;
    }

    public static function current(): ?PersonalAccessToken
    {
        $token = Context::get(self::CONTEXT_KEY);
        return $token instanceof PersonalAccessToken ? $token : null;
    }

    private static function extract(ServerRequestInterface $request): ?string
    {
        $header = $request->getHeaderLine('Authorization');
        if ($header !== '' && stripos($header, 'Bearer ') === 0) {
            return trim(substr($header, 7));
        }
        return null;
    }

    public static function clientIp(ServerRequestInterface $request): string
    {
        foreach (['X-Forwarded-For', 'X-Real-IP'] as $h) {
            $val = $request->getHeaderLine($h);
            if ($val !== '') {
                return trim(explode(',', $val)[0]);
            }
        }
        $server = $request->getServerParams();
        return $server['remote_addr'] ?? $server['REMOTE_ADDR'] ?? '0.0.0.0';
    }
}
