<?php

namespace App\Domain\Shared\Infrastructure\Persistence\Repository;

use App\Domain\Shared\Domain\Contract\FailedJobRepositoryInterface;
use App\Domain\Shared\Domain\Model\FailedJob;
use App\Domain\Shared\Domain\Model\Job;
use App\Domain\Shared\Infrastructure\Persistence\Entity\FailedJobEntity;
use App\Domain\Shared\Infrastructure\Persistence\Mapper\FailedJobMapper;
use Doctrine\ORM\EntityManagerInterface;

readonly class DoctrineFailedJobRepository implements FailedJobRepositoryInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private FailedJobMapper $mapper
    ) {
    }

    public function save(FailedJob $job): void
    {
        $entity = $this->mapper->toEntity($job);
        $this->entityManager->persist($entity);
        $this->entityManager->flush();
    }

    public function get(string $id): FailedJob
    {
        $job = $this->findById($id);

        if (null === $job) {
            throw new \RuntimeException(sprintf('Failed job with id %s not found', $id));
        }

        return $job;
    }

    public function delete(string $id): void
    {
        $entity = $this->entityManager->find(FailedJobEntity::class, $id);

        if ($entity) {
            $this->entityManager->remove($entity);
            $this->entityManager->flush();
        }
    }

    public function findAll(): array
    {
        $entities = $this->entityManager->createQueryBuilder()
            ->select('j')
            ->from(FailedJobEntity::class, 'j')
            ->getQuery()
            ->getResult();

        return array_map(fn (FailedJobEntity $entity) => $this->mapper->toDomain($entity), $entities);
    }

    public function findById(string $id): ?FailedJob
    {
        $entity = $this->entityManager->find(FailedJobEntity::class, $id);

        if (null === $entity) {
            return null;
        }

        return $this->mapper->toDomain($entity);
    }

    public function findByJobType(string $type): array
    {
        return $this->findByType($type);
    }

    public function findRetryableJobs(): array
    {
        $entities = $this->entityManager->createQueryBuilder()
            ->select('j')
            ->from(FailedJobEntity::class, 'j')
            ->getQuery()
            ->getResult();

        return array_map(
            fn (FailedJobEntity $entity) => $this->mapper->toDomain($entity),
            array_filter(
                $entities,
                fn (FailedJobEntity $entity) => $this->mapper->toDomain($entity)->attempt < 3
            )
        );
    }

    private function findByType(string $type): array
    {
        $entities = $this->entityManager->createQueryBuilder()
            ->select('j')
            ->from(FailedJobEntity::class, 'j')
            ->where('j.type = :type')
            ->setParameter('type', $type)
            ->getQuery()
            ->getResult();

        return array_map(fn (FailedJobEntity $entity) => $this->mapper->toDomain($entity), $entities);
    }
}
