<?php

declare(strict_types=1);

namespace App\Controllers;

use App\ApiClient\AdminClient;
use App\ApiClient\ApiException;
use App\Auth\SessionManager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;

/**
 * `/app/tokens` — list, create (with one-time raw-token modal), revoke.
 *
 * One-time-display flow:
 *  - POST /app/tokens calls AdminClient::createToken(); api returns the
 *    raw token in the response body.
 *  - We stash the raw token in the session under `_token_just_created`
 *    and 303 to /app/tokens.
 *  - The list view consumes the flash on render and shows it inside an
 *    Alpine modal that can't be re-opened. Refreshing the page clears
 *    the modal — the user must copy now or revoke and reissue.
 *
 * RBAC: list / create / revoke ⇒ Admin.
 */
final class TokensController
{
    use CrudControllerSupport;

    private const SESSION_KEY = '_token_just_created';

    public function __construct(
        private readonly Twig $twigEngine,
        private readonly SessionManager $sessionManager,
        private readonly AdminClient $admin,
    ) {
    }

    protected function twig(): Twig
    {
        return $this->twigEngine;
    }

    protected function sessions(): SessionManager
    {
        return $this->sessionManager;
    }

    public function index(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        $redirect = $this->requireUser($request, $response);
        if ($redirect !== null) {
            return $redirect;
        }
        $user = $this->sessionManager->getUser();
        \assert($user !== null);

        try {
            $list = $this->admin->listTokens($user->userId);
            $reportersRaw = $this->admin->listReporters($user->userId);
            $consumersRaw = $this->admin->listConsumers($user->userId);
        } catch (ApiException $e) {
            $list = ['data' => [], 'total' => 0];
            $reportersRaw = ['data' => []];
            $consumersRaw = ['data' => []];
            $this->flashFromException($e);
        }

        // One-time raw-token consumption: pop and forget.
        $justCreated = $_SESSION[self::SESSION_KEY] ?? null;
        unset($_SESSION[self::SESSION_KEY]);

        return $this->twigEngine->render($response, 'pages/tokens/index.twig', [
            'active_section' => 'tokens',
            'list' => $list,
            'just_created' => is_array($justCreated) ? $justCreated : null,
            'reporters' => $reportersRaw['data'] ?? [],
            'consumers' => $consumersRaw['data'] ?? [],
            'can_write' => $this->userIs($user, 'admin'),
        ]);
    }

    public function create(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        $redirect = $this->requireUser($request, $response);
        if ($redirect !== null) {
            return $redirect;
        }
        $user = $this->sessionManager->getUser();
        \assert($user !== null);

        $body = $this->formBody($request);
        $kind = isset($body['kind']) && is_string($body['kind']) ? $body['kind'] : '';
        $payload = ['kind' => $kind];
        if ($kind === 'reporter' && isset($body['reporter_id']) && ctype_digit((string) $body['reporter_id'])) {
            $payload['reporter_id'] = (int) $body['reporter_id'];
        }
        if ($kind === 'consumer' && isset($body['consumer_id']) && ctype_digit((string) $body['consumer_id'])) {
            $payload['consumer_id'] = (int) $body['consumer_id'];
        }
        if ($kind === 'admin' && isset($body['role']) && is_string($body['role'])) {
            $payload['role'] = trim($body['role']);
        }

        try {
            $created = $this->admin->createToken($user->userId, $payload);
            // Stash the raw token + minimal context for one-time display.
            $_SESSION[self::SESSION_KEY] = [
                'raw_token' => (string) ($created['raw_token'] ?? ''),
                'kind' => (string) ($created['kind'] ?? $kind),
                'token_prefix' => (string) ($created['token_prefix'] ?? ''),
                'id' => (int) ($created['id'] ?? 0),
            ];
            $this->sessionManager->flash('success', 'Token created. Copy it now — it won\'t be shown again.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

        return $response->withStatus(303)->withHeader('Location', '/app/tokens');
    }

    /**
     * @param array{id: string} $args
     */
    public function delete(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
    {
        $redirect = $this->requireUser($request, $response);
        if ($redirect !== null) {
            return $redirect;
        }
        $user = $this->sessionManager->getUser();
        \assert($user !== null);

        $id = $this->parseId($args['id']);
        if ($id === null) {
            return $response->withStatus(303)->withHeader('Location', '/app/tokens');
        }
        try {
            $this->admin->deleteToken($user->userId, $id);
            $this->sessionManager->flash('success', 'Token revoked.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

        return $response->withStatus(303)->withHeader('Location', '/app/tokens');
    }

    /**
     * Hard-delete a previously revoked token. The api side rejects with 409
     * on still-active tokens; we surface that as a flash on the redirect.
     *
     * @param array{id: string} $args
     */
    public function purge(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
    {
        $redirect = $this->requireUser($request, $response);
        if ($redirect !== null) {
            return $redirect;
        }
        $user = $this->sessionManager->getUser();
        \assert($user !== null);

        $id = $this->parseId($args['id']);
        if ($id === null) {
            return $response->withStatus(303)->withHeader('Location', '/app/tokens');
        }
        try {
            $this->admin->purgeToken($user->userId, $id);
            $this->sessionManager->flash('success', 'Token deleted.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

        return $response->withStatus(303)->withHeader('Location', '/app/tokens');
    }
}
