<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Helpers\ResponseHelper;
use App\Models\PersonalAccessToken;
use App\Models\User;
use App\Support\TokenAbilities;
use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;

class ApiTokenController
{
    public function catalog()
    {
        return response()->json([
            'success' => true,
            'data' => [
                'abilities' => TokenAbilities::catalog(),
                'wildcard' => TokenAbilities::WILDCARD,
            ],
        ]);
    }

    public function index(Request $request)
    {
        $tokens = PersonalAccessToken::query()
            ->where('tokenable_type', User::class)
            ->orderByDesc('id')
            ->get()
            ->map(fn ($t) => $this->present($t));

        return response()->json(['success' => true, 'data' => $tokens]);
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:120',
            'description' => 'nullable|string|max:500',
            'abilities' => 'required|array|min:1',
            'abilities.*' => 'string',
            'allowed_ips' => 'nullable|array',
            'allowed_ips.*' => 'string',
            'expires_at' => 'nullable|date',
            'tokenable_user_id' => 'nullable|integer',
        ]);

        foreach ($validated['abilities'] as $ability) {
            if (! TokenAbilities::exists($ability)) {
                return ResponseHelper::returnError("Unknown ability: {$ability}", 422);
            }
        }

        $owner = Auth::user();
        $tokenable = $owner;
        if (! empty($validated['tokenable_user_id'])) {
            $tokenable = User::query()->find($validated['tokenable_user_id']);
            if (! $tokenable) {
                return ResponseHelper::returnError('Target user not found.', 404);
            }
        }

        $result = $tokenable->createToken(
            name: $validated['name'],
            abilities: $validated['abilities'],
            allowedIps: $validated['allowed_ips'] ?? null,
            expiresAt: !empty($validated['expires_at']) ? new \DateTimeImmutable($validated['expires_at']) : null,
            description: $validated['description'] ?? null,
            createdBy: $owner->id,
        );

        return response()->json([
            'success' => true,
            'data' => [
                'token' => $this->present($result['token']),
                'plain_text_token' => $result['plainTextToken'],
            ],
            'message' => 'Token created. Copy it now — it will not be shown again.',
        ]);
    }

    public function revoke(Request $request, int $id)
    {
        $token = PersonalAccessToken::query()->find($id);
        if (! $token) {
            return ResponseHelper::returnError('Token not found.', 404);
        }
        if ($token->revoked_at !== null) {
            return ResponseHelper::returnError('Token already revoked.', 422);
        }
        $token->forceFill([
            'revoked_at' => now(),
            'revoked_by' => Auth::id(),
        ])->save();

        return response()->json(['success' => true, 'data' => $this->present($token)]);
    }

    public function destroy(Request $request, int $id)
    {
        $token = PersonalAccessToken::query()->find($id);
        if (! $token) {
            return ResponseHelper::returnError('Token not found.', 404);
        }
        $token->delete();
        return response()->json(['success' => true]);
    }

    private function present(PersonalAccessToken $t): array
    {
        return [
            'id' => $t->id,
            'name' => $t->name,
            'description' => $t->description,
            'tokenable_id' => $t->tokenable_id,
            'tokenable_type' => $t->tokenable_type,
            'abilities' => $t->abilities ?? [],
            'allowed_ips' => $t->allowed_ips ?? [],
            'expires_at' => $t->expires_at,
            'last_used_at' => $t->last_used_at,
            'last_used_ip' => $t->last_used_ip,
            'revoked_at' => $t->revoked_at,
            'created_by' => $t->created_by,
            'created_at' => $t->created_at,
            'is_active' => $t->isActive(),
        ];
    }
}
