<?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/categories` — list, edit, create, delete (with in-use guard).
 *
 * Edit page renders a small Alpine component that draws the decay
 * curve as an SVG path; pure client-side (`Decay::value` ported in JS).
 *
 * RBAC: list/show ⇒ Viewer; write ⇒ Admin.
 */
final class CategoriesController
{
    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->listCategories($user->userId);
        } catch (ApiException $e) {
            $list = ['items' => [], 'total' => 0];
            $this->flashFromException($e);
        }

        return $this->twigEngine->render($response, 'pages/categories/index.twig', [
            'active_section' => 'categories',
            'list' => $list,
            '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' => 'Category not found',
            ]);
        }
        try {
            $category = $this->admin->getCategory($user->userId, $id);
        } catch (ApiNotFoundException) {
            return $this->twigEngine->render($response->withStatus(404), 'pages/error.twig', [
                'status' => 404, 'is_client_error' => true, 'message' => 'Category not found',
            ]);
        } catch (ApiException $e) {
            $this->flashFromException($e);

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

        return $this->twigEngine->render($response, 'pages/categories/edit.twig', [
            'active_section' => 'categories',
            'category' => $category,
            '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);

        $payload = $this->payloadFromForm($this->formBody($request), true);

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

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

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

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

        $payload = $this->payloadFromForm($this->formBody($request), false);

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

        return $response->withStatus(303)->withHeader('Location', '/app/categories/' . $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/categories');
        }
        try {
            $this->admin->deleteCategory($user->userId, $id);
            $this->sessionManager->flash('success', 'Category deleted.');
        } catch (ApiException $e) {
            $this->flashFromException($e);
        }

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

    /**
     * @param array<string, mixed> $body
     * @return array<string, mixed>
     */
    private function payloadFromForm(array $body, bool $forCreate): array
    {
        $payload = [];
        if ($forCreate) {
            $payload['slug'] = isset($body['slug']) && is_string($body['slug']) ? trim($body['slug']) : '';
        } elseif (isset($body['slug']) && is_string($body['slug'])) {
            $payload['slug'] = trim($body['slug']);
        }
        if (isset($body['name']) && is_string($body['name'])) {
            $payload['name'] = trim($body['name']);
        }
        if (array_key_exists('description', $body)) {
            $payload['description'] = is_string($body['description']) && trim($body['description']) !== ''
                ? trim($body['description'])
                : null;
        }
        if (isset($body['decay_function']) && is_string($body['decay_function'])) {
            $payload['decay_function'] = trim($body['decay_function']);
        }
        if (isset($body['decay_param']) && is_numeric($body['decay_param'])) {
            $payload['decay_param'] = (float) $body['decay_param'];
        }
        $payload['is_active'] = $this->formBool($body['is_active'] ?? true);

        return $payload;
    }
}
