<?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/allowlist` — same shape as ManualBlocksController without the
 * `expires_at` field (allowlist entries don't auto-expire).
 *
 * RBAC: list ⇒ Viewer; create/delete ⇒ Operator.
 */
final class AllowlistController
{
    use CrudControllerSupport;

    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);

        $kind = $request->getQueryParams()['kind'] ?? null;
        if (!is_string($kind) || !in_array($kind, ['ip', 'subnet'], true)) {
            $kind = null;
        }

        try {
            $list = $this->admin->listAllowlist($user->userId, $kind);
        } catch (ApiException $e) {
            $list = ['items' => [], 'total' => 0];
            $this->flashFromException($e);
        }

        return $this->twigEngine->render($response, 'pages/allowlist/index.twig', [
            'active_section' => 'allowlist',
            'list' => $list,
            'kind' => $kind,
            'can_write' => $this->userIs($user, 'operator', '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 === 'ip' && isset($body['ip']) && is_string($body['ip'])) {
            $payload['ip'] = trim($body['ip']);
        }
        if ($kind === 'subnet' && isset($body['cidr']) && is_string($body['cidr'])) {
            $payload['cidr'] = trim($body['cidr']);
        }
        if (isset($body['reason']) && is_string($body['reason']) && trim($body['reason']) !== '') {
            $payload['reason'] = trim($body['reason']);
        }

        try {
            $this->admin->createAllowlist($user->userId, $payload);
            $this->sessionManager->flash('success', 'Allowlist entry added.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

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

    /**
     * @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/allowlist');
        }

        try {
            $this->admin->deleteAllowlist($user->userId, $id);
            $this->sessionManager->flash('success', 'Allowlist entry removed.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

        // SEC_REVIEW F10: route the attacker-influenced `next` form field
        // through the same-origin path validator before using it as a
        // `Location` value.
        $next = SessionManager::safeNextOrDefault(
            $this->formBody($request)['next'] ?? null,
            '/app/allowlist',
        );

        return $response->withStatus(303)->withHeader('Location', $next);
    }
}
