<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Controller\Admin;

use CpCreation\VitiCore\Versioning\Model\BusinessVersion;
use CpCreation\VitiCore\Versioning\Repository\BusinessVersionRepository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/business-versions')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_SUPER_ADMIN')"))]
final class BusinessVersionController extends AbstractController
{
    private const PAGE_SIZE = 25;

    public function __construct(
        private readonly BusinessVersionRepository $businessVersionRepository,
        private readonly PaginatorInterface $paginator,
    ) {
    }

    #[Route('', name: 'admin_business_version')]
    public function index(Request $request): Response
    {
        $page = max(1, $request->query->getInt('page', 1));
        $filters = $this->extractFilters($request);
        $queryBuilder = $this->businessVersionRepository->createAdminQueryBuilder($filters);

        return $this->render('@CpCreationVitiCore/admin/business_version/index.html.twig', [
            'versions' => $this->paginator->paginate($queryBuilder, $page, self::PAGE_SIZE),
            'filters' => $filters,
            'filterQuery' => $this->buildFilterQuery($filters),
            'scopes' => $this->businessVersionRepository->findDistinctScopes(),
            'objectClasses' => $this->businessVersionRepository->findDistinctObjectClasses(),
            'statuses' => [
                BusinessVersion::STATUS_PUBLISHED => 'Publiée',
                BusinessVersion::STATUS_ARCHIVED => 'Archivée',
                BusinessVersion::STATUS_DRAFT => 'Brouillon',
            ],
        ]);
    }

    #[Route('/{id}', name: 'admin_business_version_show')]
    public function show(BusinessVersion $version, Request $request): Response
    {
        return $this->render('@CpCreationVitiCore/admin/business_version/show.html.twig', [
            'version' => $version,
            'history' => $this->businessVersionRepository->findObjectHistory(
                $version->getObjectClass(),
                $version->getObjectId(),
                $version->getScope(),
            ),
            'returnParams' => $this->extractReturnQuery($request),
        ]);
    }

    #[Route('/{id}/delete', name: 'admin_business_version_delete')]
    public function delete(BusinessVersion $version, Request $request): Response
    {
        if (BusinessVersion::STATUS_DRAFT !== $version->getStatus()) {
            $this->addFlash('alert-warning', 'Seules les planifications en brouillon peuvent être supprimées.');

            return $this->redirect($this->resolveReturnUrl($request));
        }

        $this->businessVersionRepository->delete($version);
        $this->addFlash('alert-success', 'La planification a été supprimée.');

        return $this->redirect($this->resolveReturnUrl($request));
    }

    /**
     * @return array{
     *     scope: ?string,
     *     status: ?string,
     *     objectClass: ?string,
     *     objectId: ?string,
     *     createdBy: ?string,
     *     createdFrom: ?\DateTimeImmutable,
     *     createdTo: ?\DateTimeImmutable,
     *     createdFromInput: ?string,
     *     createdToInput: ?string
     * }
     */
    private function extractFilters(Request $request): array
    {
        $createdFrom = $this->normalizeDateTime($request->query->get('createdFrom'));
        $createdTo = $this->normalizeDateTime($request->query->get('createdTo'));

        return [
            'scope' => $this->normalizeNullableString($request->query->get('scope')),
            'status' => $this->normalizeStatus($request->query->get('status')),
            'objectClass' => $this->normalizeNullableString($request->query->get('objectClass')),
            'objectId' => $this->normalizeNullableString($request->query->get('objectId')),
            'createdBy' => $this->normalizeNullableString($request->query->get('createdBy')),
            'createdFrom' => $createdFrom,
            'createdTo' => $createdTo,
            'createdFromInput' => $this->formatDateTimeInput($createdFrom),
            'createdToInput' => $this->formatDateTimeInput($createdTo),
        ];
    }

    /**
     * @param array<string, mixed> $filters
     *
     * @return array<string, string>
     */
    private function buildFilterQuery(array $filters): array
    {
        $query = [
            'scope' => $filters['scope'] ?? null,
            'status' => $filters['status'] ?? null,
            'objectClass' => $filters['objectClass'] ?? null,
            'objectId' => $filters['objectId'] ?? null,
            'createdBy' => $filters['createdBy'] ?? null,
            'createdFrom' => $filters['createdFromInput'] ?? null,
            'createdTo' => $filters['createdToInput'] ?? null,
        ];

        return array_filter(
            $query,
            static fn (?string $value): bool => null !== $value && '' !== $value,
        );
    }

    /**
     * @return array<string, string>
     */
    private function extractReturnQuery(Request $request): array
    {
        return array_filter(
            [
                'scope' => $this->normalizeNullableString($request->query->get('scope')),
                'status' => $this->normalizeStatus($request->query->get('status')),
                'objectClass' => $this->normalizeNullableString($request->query->get('objectClass')),
                'objectId' => $this->normalizeNullableString($request->query->get('objectId')),
                'createdBy' => $this->normalizeNullableString($request->query->get('createdBy')),
                'createdFrom' => $this->formatDateTimeInput($this->normalizeDateTime($request->query->get('createdFrom'))),
                'createdTo' => $this->formatDateTimeInput($this->normalizeDateTime($request->query->get('createdTo'))),
                'page' => $this->normalizeNullableString($request->query->get('page')),
            ],
            static fn (?string $value): bool => null !== $value && '' !== $value,
        );
    }

    private function normalizeNullableString(mixed $value): ?string
    {
        if (!\is_string($value)) {
            return null;
        }

        $value = trim($value);

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

    private function normalizeStatus(mixed $status): ?string
    {
        if (!\is_string($status)) {
            return null;
        }

        return \in_array($status, [
            BusinessVersion::STATUS_DRAFT,
            BusinessVersion::STATUS_PUBLISHED,
            BusinessVersion::STATUS_ARCHIVED,
        ], true) ? $status : null;
    }

    private function normalizeDateTime(mixed $value): ?\DateTimeImmutable
    {
        if (!\is_string($value)) {
            return null;
        }

        $value = trim($value);
        if ('' === $value) {
            return null;
        }

        try {
            return new \DateTimeImmutable($value);
        } catch (\Throwable) {
            return null;
        }
    }

    private function formatDateTimeInput(?\DateTimeImmutable $dateTime): ?string
    {
        return $dateTime?->format('Y-m-d\TH:i');
    }

    private function resolveReturnUrl(Request $request): string
    {
        $returnUrl = $request->query->get('returnUrl');
        if (\is_string($returnUrl) && str_starts_with($returnUrl, '/') && !str_starts_with($returnUrl, '//')) {
            return $returnUrl;
        }

        return $this->generateUrl('admin_business_version');
    }
}
