<?php

namespace NoahVet\Reef\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseController;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Yaml\Yaml;

/**
 * @psalm-import-type WhitelistShape from \NoahVet\Reef\Factory\RequestContext\ApplierInterface
 */
abstract class AbstractController extends BaseController implements ControllerInterface
{
    /**
     * @var array<string, mixed>
     */
    protected array $headers = [];

    /**
     * @var WhitelistShape
     */
    protected array $whitelist = [
        'filters' => [],
        'pagination' => [],
        'permissions' => [],
        'sorting' => [],
    ];

    /**
     * @psalm-suppress UninitializedProperty - Covered by "calls:" in Resources/config/service/controller.yaml
     * @psalm-suppress PropertyNotSetInConstructor - Covered by "calls:" in Resources/config/service/controller.yaml
     */
    protected Request $request;

    #[\Override]
    public function setRequest(Request $request): void
    {
        $this->request = $request;
    }

    #[\Override]
    public function preInvoke(): void
    {
    }

    #[\Override]
    public function postInvoke(): void
    {
    }

    protected function setHeader(string $name, string $value): self
    {
        $this->headers[$name] = $value;

        return $this;
    }

    /**
     * @return array<string, mixed>
     */
    protected function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * @return list<string>
     */
    protected function parseQueryParamFile(string $uri): array
    {
        return Yaml::parseFile(
            \sprintf(
                '/var/www/doc/OpenAPI/Generated/QueryParam%s/%s/parameter.yaml',
                $uri,
                \strtolower($this->request->getMethod()),
            ),
        )['parameters'];
    }

    protected function getNoContentResponse(): Response
    {
        return new JsonResponse(
            null,
            Response::HTTP_NO_CONTENT,
            $this->headers,
        );
    }

    protected function mapQueryParamToWhitelist(): void
    {
        $uri = \explode(
            '?',
            \preg_replace(
                '/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/',
                'id',
                $this->request->getRequestUri(),
            ) ?? '',
        )[0];

        try {
            $queryParamFileContent = $this->parseQueryParamFile($uri);
            $queryParamFileContent = \array_flip($queryParamFileContent);
        } catch (\Exception $e) {
            throw new FileNotFoundException('Cannot open the associated parameter.yaml file.');
        }

        $this->whitelist['pagination']['page'] = $queryParamFileContent['page'];
        $this->whitelist['pagination']['limit'] = $queryParamFileContent['limit'];

        unset($queryParamFileContent['page']);
        unset($queryParamFileContent['limit']);

        /** @var array{filters?: list<string>, sorting?: list<string>} $queryParamFileContentWhitelist */
        $queryParamFileContentWhitelist = [];

        foreach ($queryParamFileContent as $queryParamFileContentKey => $queryParamFileContentValue) {
            $key = 'filters';
            if (\str_starts_with($queryParamFileContentKey, 'sort')) {
                $key = 'sorting';
            }
            $queryParamFileContentWhitelist[$key][] = $queryParamFileContentKey;
            unset($queryParamFileContent[$queryParamFileContentKey]);
        }
        $this->whitelist = \array_merge($this->whitelist, $queryParamFileContentWhitelist);
        \ksort($this->whitelist);
    }
}
