<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1\Account;

use App\Enums\AuthEventType;
use App\Services\Auth\AuthEventRecorder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Symfony\Component\HttpFoundation\Response;

/**
 * Lets a user see and revoke the API tokens issued to their devices.
 *
 * Self-service revocation matters here: if a phone holding a token is lost, the
 * owner should not have to wait on support to cut it off from a portfolio.
 */
final class DeviceController extends Controller
{
    public function __construct(
        private readonly AuthEventRecorder $authEvents,
    ) {}

    public function index(Request $request): JsonResponse
    {
        $user = $request->user();
        $current = $user->currentAccessToken();

        $devices = $user->tokens()
            ->orderByDesc('last_used_at')
            ->orderByDesc('created_at')
            ->get()
            ->map(fn ($token): array => [
                'id' => $token->getKey(),
                'name' => $token->device_name ?: $token->name,
                'platform' => $token->device_platform,
                'last_used_at' => $token->last_used_at?->toIso8601String(),
                'last_used_ip' => $token->last_used_ip,
                'created_at' => $token->created_at?->toIso8601String(),
                'expires_at' => $token->expires_at?->toIso8601String(),
                'is_current' => $current !== null && $current->getKey() === $token->getKey(),
            ]);

        return response()->json(['data' => $devices]);
    }

    public function destroy(Request $request, int $token): JsonResponse
    {
        $user = $request->user();

        // Scoped to the caller's own tokens, so an id from elsewhere simply
        // does not exist as far as this endpoint is concerned.
        $deleted = $user->tokens()->whereKey($token)->delete();

        if ($deleted === 0) {
            return response()->json(['message' => 'Device not found.'], Response::HTTP_NOT_FOUND);
        }

        $this->authEvents->record(AuthEventType::TokenRevoked, $user, metadata: ['token_id' => $token]);

        return response()->json(['message' => 'Device signed out.']);
    }

    /** Revokes every token except the one making the request. */
    public function destroyOthers(Request $request): JsonResponse
    {
        $user = $request->user();
        $current = $user->currentAccessToken();

        $count = $user->tokens()
            ->when($current !== null, fn ($query) => $query->whereKeyNot($current->getKey()))
            ->delete();

        $this->authEvents->record(AuthEventType::TokenRevoked, $user, metadata: ['revoked_count' => $count]);

        return response()->json([
            'message' => $count === 0
                ? 'No other devices were signed in.'
                : sprintf('%d other device%s signed out.', $count, $count === 1 ? '' : 's'),
        ]);
    }
}
