<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\Contract\AuditLogReadRepositoryInterface;
use DateTimeImmutable;
use DateTimeZone;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;

class AuditLogProvider implements ProviderInterface
{
    private const DEFAULT_PER_PAGE = 10;
    private const ALLOWED_PER_PAGE = [10, 25, 50, 100];

    public function __construct(
        private readonly RequestStack $requestStack,
        private readonly AuditLogReadRepositoryInterface $auditLogRepository,
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): JsonResponse
    {
        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return new JsonResponse(['items' => [], 'total' => 0]);
        }

        $query = $request->query;
        $all   = $query->all();

        $employeeId = $query->get('employeeId');
        $from       = $query->get('from');
        $to         = $query->get('to');
        $page       = max(1, (int) $query->get('page', '1'));

        $perPage = (int) $query->get('perPage', (string) self::DEFAULT_PER_PAGE);
        if (!in_array($perPage, self::ALLOWED_PER_PAGE, true)) {
            $perPage = self::DEFAULT_PER_PAGE;
        }

        $entityTypes = $this->normalizeList($all['entityType'] ?? null);
        $actions     = $this->normalizeList($all['action'] ?? null);
        $username    = $this->normalizeString($query->get('username'));
        $ip          = $this->normalizeString($query->get('ip'));
        $device      = $this->normalizeString($query->get('device'));
        $employee    = $this->normalizeString($query->get('employee'));

        $empId  = $employeeId ? (int) $employeeId : null;
        $fromDt = $from ? new DateTimeImmutable((string) $from) : null;
        $toDt   = $to ? new DateTimeImmutable((string) $to) : null;
        $offset = ($page - 1) * $perPage;

        $total = $this->auditLogRepository->countByFilters($empId, $fromDt, $toDt, $entityTypes, $actions, $username, $ip, $device, $employee);
        $logs  = $this->auditLogRepository->findByFilters($empId, $fromDt, $toDt, $entityTypes, $actions, $username, $ip, $device, $employee, $perPage, $offset);

        $items = [];
        foreach ($logs as $log) {
            $employee     = $log->getEmployee();
            $employeeName = $employee
                ? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''))
                : null;

            $items[] = [
                'id'           => $log->getId(),
                'employeeName' => $employeeName,
                'employeeId'   => $employee?->getId(),
                'username'     => $log->getUsername(),
                'action'       => $log->getAction(),
                'entityType'   => $log->getEntityType(),
                'description'  => $log->getDescription(),
                'changes'      => $log->getChanges(),
                'affectedDate' => $log->getAffectedDate()?->format('Y-m-d'),
                'ipAddress'    => $log->getIpAddress(),
                'userAgent'    => $log->getUserAgent(),
                'deviceLabel'  => $log->getDeviceLabel(),
                'deviceId'     => $log->getDeviceId(),
                'createdAt'    => $log->getCreatedAt()->setTimezone(new DateTimeZone('Europe/Paris'))->format('Y-m-d H:i:s'),
            ];
        }

        return new JsonResponse([
            'items'   => $items,
            'total'   => $total,
            'page'    => $page,
            'perPage' => $perPage,
        ]);
    }

    /**
     * @return null|list<string>
     */
    private function normalizeList(mixed $value): ?array
    {
        $list = array_values(array_filter(
            (array) ($value ?? []),
            static fn ($v): bool => is_string($v) && '' !== trim($v),
        ));

        return [] === $list ? null : $list;
    }

    private function normalizeString(mixed $value): ?string
    {
        $trimmed = trim((string) ($value ?? ''));

        return '' === $trimmed ? null : $trimmed;
    }
}
