<?php

declare(strict_types=1);

namespace App\Controller\Admin;

use App\Entity\TagRebuildJob;
use App\Service\Admin\TagAdminService;
use App\Tag\TagTypes;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/admin/tags')]
final class TagController extends AbstractController
{
    #[Route('', name: 'admin_tags_index', methods: ['GET'])]
    public function index(TagAdminService $svc): Response
    {
        return $this->render('admin/tag/index.html.twig', [
            ...$svc->getIndexData(),
            ...$this->buildJobStatusViewData(),
        ]);
    }

    #[Route('/create', name: 'admin_tags_create', methods: ['POST'])]
    public function create(Request $request, TagAdminService $svc): RedirectResponse
    {
        if (!$this->isCsrfTokenValid('admin_tag_create', (string) $request->request->get('_token'))) {
            $this->addFlash('danger', 'Ungültiges CSRF-Token.');

            return $this->redirectToRoute('admin_tags_index');
        }

        try {
            $svc->create(
                (string) $request->request->get('slug', ''),
                (string) $request->request->get('label', ''),
                $this->normalizeNullableString($request->request->get('description')),
                TagTypes::normalize((string) $request->request->get('type', TagTypes::GENERIC))
            );

            $this->addFlash('success', 'Tag wurde erstellt.');
        } catch (\Throwable $e) {
            $this->addFlash('danger', $this->buildSafeErrorMessage($e, 'Tag konnte nicht erstellt werden.'));
        }

        return $this->redirectToRoute('admin_tags_index');
    }

    #[Route('/{id}/delete', name: 'admin_tags_delete', methods: ['POST'])]
    public function delete(string $id, Request $request, TagAdminService $svc): RedirectResponse
    {
        if (!$this->isCsrfTokenValid('admin_tag_delete_' . $id, (string) $request->request->get('_token'))) {
            $this->addFlash('danger', 'Ungültiges CSRF-Token.');

            return $this->redirectToRoute('admin_tags_index');
        }

        try {
            $svc->delete(trim($id));
            $this->addFlash('success', 'Tag wurde gelöscht.');
        } catch (\Throwable $e) {
            $this->addFlash('danger', $this->buildSafeErrorMessage($e, 'Tag konnte nicht gelöscht werden.'));
        }

        return $this->redirectToRoute('admin_tags_index');
    }

    #[Route('/{id}/assign', name: 'admin_tags_assign', methods: ['GET', 'POST'])]
    public function assign(string $id, Request $request, TagAdminService $svc): Response
    {
        $id = trim($id);

        if ($request->isMethod('POST')) {
            if (!$this->isCsrfTokenValid('assign_tag_' . $id, (string) $request->request->get('_token'))) {
                $this->addFlash('danger', 'Ungültiges CSRF-Token.');

                return $this->redirectToRoute('admin_tags_assign', ['id' => $id]);
            }

            try {
                $svc->syncAssignments($id, $this->normalizeStringList($request->request->all('documents')));
                $this->addFlash('success', 'Zuweisungen aktualisiert.');
            } catch (\Throwable $e) {
                $this->addFlash('danger', $this->buildSafeErrorMessage($e, 'Zuweisungen konnten nicht aktualisiert werden.'));
            }

            return $this->redirectToRoute('admin_tags_assign', ['id' => $id]);
        }

        try {
            $data = $svc->getAssignData($id);
        } catch (\Throwable $e) {
            $this->addFlash('danger', $this->buildSafeErrorMessage($e, 'Tag konnte nicht geladen werden.'));

            return $this->redirectToRoute('admin_tags_index');
        }

        return $this->render('admin/tag/assign.html.twig', [
            ...$data,
            ...$this->buildJobStatusViewData(),
        ]);
    }

    /**
     * @param mixed $value
     */
    private function normalizeNullableString(mixed $value): ?string
    {
        $value = trim((string) $value);

        return $value !== '' ? $value : null;
    }

    /**
     * @param mixed $values
     * @return list<string>
     */
    private function normalizeStringList(mixed $values): array
    {
        if (!is_array($values)) {
            return [];
        }

        $normalized = [];

        foreach ($values as $value) {
            $value = trim((string) $value);

            if ($value === '') {
                continue;
            }

            $normalized[] = $value;
        }

        return array_values(array_unique($normalized));
    }

    /**
     * @return array<string, string>
     */
    private function buildJobStatusViewData(): array
    {
        return [
            'statusRunning' => TagRebuildJob::STATUS_RUNNING,
            'statusQueued' => TagRebuildJob::STATUS_QUEUED,
            'statusCompleted' => TagRebuildJob::STATUS_COMPLETED,
            'statusFailed' => TagRebuildJob::STATUS_FAILED,
        ];
    }

    private function buildSafeErrorMessage(\Throwable $e, string $fallback): string
    {
        $message = trim($e->getMessage());

        return $message !== '' ? $message : $fallback;
    }
}