<?php

declare(strict_types=1);

namespace App\Controllers;

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

/**
 * `/app/consumers` — list, edit, create, soft-delete. Edit page exposes
 * a policy dropdown populated from `listPolicies()`.
 *
 * RBAC: list/show ⇒ Viewer; write ⇒ Admin.
 */
final class ConsumersController
{
    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);

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

        return $this->twigEngine->render($response, 'pages/consumers/index.twig', [
            'active_section' => 'consumers',
            'list' => $list,
            'policies' => $policiesRaw['items'] ?? [],
            'can_write' => $this->userIs($user, 'admin'),
        ]);
    }

    /**
     * @param array{id: string} $args
     */
    public function edit(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 $this->twigEngine->render($response->withStatus(404), 'pages/error.twig', [
                'status' => 404, 'is_client_error' => true, 'message' => 'Consumer not found',
            ]);
        }
        try {
            $consumer = $this->admin->getConsumer($user->userId, $id);
            $policiesRaw = $this->admin->listPolicies($user->userId);
        } catch (ApiNotFoundException) {
            return $this->twigEngine->render($response->withStatus(404), 'pages/error.twig', [
                'status' => 404, 'is_client_error' => true, 'message' => 'Consumer not found',
            ]);
        } catch (ApiException $e) {
            $this->flashFromException($e);

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

        $auditItems = [];
        try {
            // `subject_*` matches both events targeting this consumer (admin
            // CRUD) and events the consumer emitted itself
            // (`blocklist.requested`), so the activity table is a single
            // chronological view.
            $auditPage = $this->admin->listAuditLog(
                $user->userId,
                ['subject_kind' => 'consumer', 'subject_id' => (string) $id],
                1,
                10,
            );
            $auditItems = is_array($auditPage['items'] ?? null) ? $auditPage['items'] : [];
        } catch (ApiException) {
            // Audit lookup is best-effort; the edit form must still render
            // if the audit endpoint is unavailable.
        }

        return $this->twigEngine->render($response, 'pages/consumers/edit.twig', [
            'active_section' => 'consumers',
            'consumer' => $consumer,
            'policies' => $policiesRaw['items'] ?? [],
            'can_write' => $this->userIs($user, 'admin'),
            'audit_items' => $auditItems,
        ]);
    }

    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);
        $payload = [
            'name' => isset($body['name']) && is_string($body['name']) ? trim($body['name']) : '',
        ];
        if (isset($body['description']) && is_string($body['description'])) {
            $payload['description'] = trim($body['description']) === '' ? null : trim($body['description']);
        }
        if (isset($body['policy_id']) && (string) $body['policy_id'] !== '' && ctype_digit((string) $body['policy_id'])) {
            $payload['policy_id'] = (int) $body['policy_id'];
        }

        try {
            $created = $this->admin->createConsumer($user->userId, $payload);
            $this->sessionManager->flash('success', 'Consumer created.');
            $newId = (int) ($created['id'] ?? 0);

            return $response->withStatus(303)->withHeader('Location', $newId > 0 ? '/app/consumers/' . $newId : '/app/consumers');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

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

    /**
     * @param array{id: string} $args
     */
    public function update(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/consumers');
        }
        $body = $this->formBody($request);
        $payload = [];
        if (isset($body['name']) && is_string($body['name'])) {
            $payload['name'] = trim($body['name']);
        }
        if (isset($body['description']) && is_string($body['description'])) {
            $payload['description'] = trim($body['description']) === '' ? null : trim($body['description']);
        }
        if (isset($body['policy_id']) && (string) $body['policy_id'] !== '' && ctype_digit((string) $body['policy_id'])) {
            $payload['policy_id'] = (int) $body['policy_id'];
        }
        if (array_key_exists('is_active', $body)) {
            $payload['is_active'] = $this->formBool($body['is_active']);
        }
        if (array_key_exists('audit_enabled', $body)) {
            $payload['audit_enabled'] = $this->formBool($body['audit_enabled']);
        }

        try {
            $this->admin->updateConsumer($user->userId, $id, $payload);
            $this->sessionManager->flash('success', 'Consumer saved.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

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

    /**
     * @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/consumers');
        }
        try {
            $this->admin->deleteConsumer($user->userId, $id);
            $this->sessionManager->flash('success', 'Consumer deactivated.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

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