<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocStoreBundle\Repository;

use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;

/**
 * @implements ObjectRepository<StoredObjectVersion>
 */
class StoredObjectVersionRepository implements ObjectRepository
{
    private readonly EntityRepository $repository;

    private readonly Connection $connection;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(StoredObjectVersion::class);
        $this->connection = $entityManager->getConnection();
    }

    public function find($id): ?StoredObjectVersion
    {
        return $this->repository->find($id);
    }

    public function findAll(): array
    {
        return $this->repository->findAll();
    }

    public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
    {
        return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
    }

    public function findOneBy(array $criteria): ?StoredObjectVersion
    {
        return $this->repository->findOneBy($criteria);
    }

    /**
     * Finds the IDs of versions older than a given date and that are not the last version.
     *
     * Those version are good candidates for a deletion.
     *
     * @param \DateTimeImmutable $beforeDate the date to compare versions against
     *
     * @return iterable returns an iterable with the IDs of the versions
     */
    public function findIdsByVersionsOlderThanDateAndNotLastVersionAndNotPointInTime(\DateTimeImmutable $beforeDate): iterable
    {
        $results = $this->connection->executeQuery(
            self::QUERY_FIND_IDS_BY_VERSIONS_OLDER_THAN_DATE_AND_NOT_LAST_VERSION,
            [$beforeDate],
            [Types::DATETIME_IMMUTABLE]
        );

        foreach ($results->iterateAssociative() as $row) {
            yield $row['sov_id'];
        }
    }

    private const QUERY_FIND_IDS_BY_VERSIONS_OLDER_THAN_DATE_AND_NOT_LAST_VERSION = <<<'SQL'
        SELECT
            sov.id AS sov_id
        FROM chill_doc.stored_object_version sov
        WHERE
            sov.createdat < ?::timestamp
            AND
            sov.version < (SELECT MAX(sub_sov.version) FROM chill_doc.stored_object_version sub_sov WHERE sub_sov.stored_object_id = sov.stored_object_id)
            AND
            NOT EXISTS (SELECT 1 FROM chill_doc.stored_object_point_in_time sub_poi WHERE sub_poi.stored_object_version_id = sov.id)
        SQL;

    public function getClassName(): string
    {
        return StoredObjectVersion::class;
    }
}
