<?php

namespace App\Service\SnipParser\Generic;

use App\Repository\SnipContentRepository;
use App\Repository\SnipRepository;
use App\Security\Voter\SnipVoter;
use App\Service\SnipContent\SnipContentService;
use League\Pipeline\StageInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class IncludeReferenceStage implements StageInterface
{
    public function __construct(
        #[Autowire(lazy: true)] private readonly Security              $security,
        #[Autowire(lazy: true)] private readonly SnipRepository        $snipRepository,
        #[Autowire(lazy: true)] private readonly SnipContentRepository $snipContentRepository,
        #[Autowire(lazy: true)] private readonly GenericParser         $pipeline,
    ) {}

    public function __invoke(mixed $payload): string
    {
        return $this->replaceReferences($payload);
    }

    private function replaceReferences(mixed $payload): string
    {
        // replaces all references ({{ID}}) with the content of the snip
        $pattern = '/\{\{([A-Z0-9]+)\}\}/';

        return preg_replace_callback($pattern, function ($matches) {
            $id = $matches[1];
            try {
                $content = $this->snipContentRepository->find($id);
            } catch (\Exception) {
                $content = null;
            }
            if ($content) {
                $snip = $content->snip;
            } else {
                $snip = $this->snipRepository->find($id);
                if ($snip) {
                    $content = $this->snipContentRepository->find($snip->activeVersion);
                }
            }
            if ($content === null) {
                return sprintf('<span title="snip or content not found">%s</span>', $matches[0]);
            }
            if (!$this->security->isGranted(SnipVoter::VIEW, $snip)) {
                return sprintf('<span title="access denied">%s</span>', $matches[0]);
            }

            return $this->pipeline->parseView(
                SnipContentService::rebuildText($content)
            );
        }, $payload);
    }
}
