<?php

declare(strict_types=1);

namespace App\Support;

use App\Models\PersonalAccessToken;
use Hyperf\Database\Model\Relations\MorphMany;

trait HasApiTokens
{
    public function tokens(): MorphMany
    {
        return $this->morphMany(PersonalAccessToken::class, 'tokenable');
    }

    public function currentAccessToken(): ?PersonalAccessToken
    {
        return $this->accessToken ?? null;
    }

    public function withAccessToken(?PersonalAccessToken $token): static
    {
        $this->accessToken = $token;
        return $this;
    }

    /**
     * Create a new token.
     *
     * @param array<int, string> $abilities
     * @param array<int, string> $allowedIps
     * @return array{token: PersonalAccessToken, plainTextToken: string}
     */
    public function createToken(
        string $name,
        array $abilities,
        ?array $allowedIps = null,
        ?\DateTimeInterface $expiresAt = null,
        ?string $description = null,
        ?int $createdBy = null,
    ): array {
        foreach ($abilities as $ability) {
            if (! TokenAbilities::exists($ability)) {
                throw new \InvalidArgumentException("Unknown ability: {$ability}");
            }
        }

        $plain = bin2hex(random_bytes(32));

        $token = $this->tokens()->create([
            'name' => $name,
            'description' => $description,
            'token' => hash('sha256', $plain),
            'abilities' => array_values(array_unique($abilities)),
            'allowed_ips' => $allowedIps ? array_values(array_filter(array_map('trim', $allowedIps))) : null,
            'expires_at' => $expiresAt,
            'created_by' => $createdBy,
        ]);

        return [
            'token' => $token,
            'plainTextToken' => $token->id . '|' . $plain,
        ];
    }
}
