<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\AuditLog;
use App\Repository\Contract\AuditLogReadRepositoryInterface;
use DateTimeImmutable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<AuditLog>
 */
final class AuditLogRepository extends ServiceEntityRepository implements AuditLogReadRepositoryInterface
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, AuditLog::class);
    }

    public function findByFilters(
        ?int $employeeId = null,
        ?DateTimeImmutable $from = null,
        ?DateTimeImmutable $to = null,
        ?array $entityTypes = null,
        ?array $actions = null,
        ?string $username = null,
        ?string $ip = null,
        ?string $device = null,
        ?string $employeeName = null,
        int $limit = 50,
        int $offset = 0,
    ): array {
        $qb = $this->createQueryBuilder('a')
            ->orderBy('a.createdAt', 'DESC')
            ->setMaxResults($limit)
            ->setFirstResult($offset)
        ;
        $this->applyFilters($qb, $employeeId, $from, $to, $entityTypes, $actions, $username, $ip, $device, $employeeName);

        return $qb->getQuery()->getResult();
    }

    public function countByFilters(
        ?int $employeeId = null,
        ?DateTimeImmutable $from = null,
        ?DateTimeImmutable $to = null,
        ?array $entityTypes = null,
        ?array $actions = null,
        ?string $username = null,
        ?string $ip = null,
        ?string $device = null,
        ?string $employeeName = null,
    ): int {
        $qb = $this->createQueryBuilder('a')->select('COUNT(a.id)');
        $this->applyFilters($qb, $employeeId, $from, $to, $entityTypes, $actions, $username, $ip, $device, $employeeName);

        return (int) $qb->getQuery()->getSingleScalarResult();
    }

    /**
     * @param null|list<string> $entityTypes
     * @param null|list<string> $actions
     */
    private function applyFilters(
        QueryBuilder $qb,
        ?int $employeeId,
        ?DateTimeImmutable $from,
        ?DateTimeImmutable $to,
        ?array $entityTypes,
        ?array $actions,
        ?string $username,
        ?string $ip,
        ?string $device,
        ?string $employeeName = null,
    ): void {
        if (null !== $employeeId) {
            $qb->andWhere('a.employee = :employeeId')->setParameter('employeeId', $employeeId);
        }
        if (null !== $employeeName && '' !== $employeeName) {
            $qb->join('a.employee', 'e')
                ->andWhere('LOWER(e.lastName) LIKE :employeeName OR LOWER(e.firstName) LIKE :employeeName')
                ->setParameter('employeeName', '%'.mb_strtolower($employeeName).'%')
            ;
        }
        if (null !== $from) {
            $qb->andWhere('a.affectedDate >= :from')->setParameter('from', $from);
        }
        if (null !== $to) {
            $qb->andWhere('a.affectedDate <= :to')->setParameter('to', $to);
        }
        if (null !== $entityTypes && [] !== $entityTypes) {
            $qb->andWhere('a.entityType IN (:entityTypes)')->setParameter('entityTypes', $entityTypes);
        }
        if (null !== $actions && [] !== $actions) {
            $qb->andWhere('a.action IN (:actions)')->setParameter('actions', $actions);
        }
        if (null !== $username && '' !== $username) {
            $qb->andWhere('LOWER(a.username) LIKE :username')->setParameter('username', '%'.mb_strtolower($username).'%');
        }
        if (null !== $ip && '' !== $ip) {
            $qb->andWhere('LOWER(a.ipAddress) LIKE :ip')->setParameter('ip', '%'.mb_strtolower($ip).'%');
        }
        if (null !== $device && '' !== $device) {
            $qb->andWhere('(LOWER(a.deviceLabel) LIKE :device OR LOWER(a.deviceId) LIKE :device)')
                ->setParameter('device', '%'.mb_strtolower($device).'%')
            ;
        }
    }
}
