<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Repository;

use CpCreation\VitiCore\Behavior\HasDisable;
use CpCreation\VitiCore\Behavior\HasSortable;
use CpCreation\VitiCore\Behavior\IsSoftDeletable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectRepository;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\PaginatorInterface;
use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Symfony\Contracts\Service\Attribute\Required;

abstract class BaseRepository extends ServiceEntityRepository implements ObjectRepository
{
    private \ReflectionClass $entity;

    /**
     * @throws \ReflectionException
     */
    public function __construct(ManagerRegistry $registry, string $entityClass)
    {
        parent::__construct($registry, $entityClass);
        $this->entity = new \ReflectionClass($entityClass);
    }

    public function persist(object $object): void
    {
        $this->getEntityManager()->persist($object);
    }

    public function flush(): void
    {
        $this->getEntityManager()->flush();
    }

    public function save(object $object): void
    {
        $this->getEntityManager()->persist($object);
        $this->getEntityManager()->flush();
    }

    public function delete(object $object): void
    {
        $this->getEntityManager()->remove($object);
        $this->getEntityManager()->flush();
    }

    public function softDelete(object $object): void
    {
        $this->getEntityManager()->remove($object);
    }

    public function disable(object $object): object|false
    {
        if ($object instanceof HasDisable) {
            $object->setDisabled(true);
            $this->save($object);

            return $object;
        }

        return false;
    }

    public function enable(object $object): object|false
    {
        if ($object instanceof HasDisable) {
            $object->setDisabled(false);
            $this->save($object);

            return $object;
        }

        return false;
    }

    public function moveUp(object $object): object|false
    {
        if ($object instanceof HasSortable) {
            if ($object->getPosition() > 0) {
                $object->setPosition($object->getPosition() - 1);
                $this->save($object);

                return $object;
            }

            return false;
        }

        return false;
    }

    public function moveDown(object $object): object|false
    {
        if ($object instanceof HasSortable) {
            if ($object->getPosition() > 0) {
                $object->setPosition($object->getPosition() + 1);
                $this->save($object);

                return $object;
            }

            return false;
        }

        return false;
    }

    public function findByCreatedAdminAt(string $sort): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.createdAt', $sort);

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByCreatedAt(string $sort): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.createdAt', $sort);

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByPositionOrderAdmin(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.position', 'ASC');

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByPositionOrder(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.position', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByPositionOrderQuery(): QueryBuilder
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.position', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

        return $qb;
    }

    public function findByTitleOrderAdmin(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.title', 'ASC');

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByTitleOrderAdminQuery(): QueryBuilder
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.title', 'ASC');

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        return $qb;
    }

    public function findByTitleOrder(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.title', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByTitleOrderQuery(): QueryBuilder
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.title', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

        return $qb;
    }

    public function findByNameOrderAdmin(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.name', 'ASC');

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByNameOrderAdminQuery(): QueryBuilder
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.name', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

        return $qb;
    }

    public function findByNameOrder(): array
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.name', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    public function findByNameOrderQuery(): QueryBuilder
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.name', 'ASC');

        if ($this->isDisableable()) {
            $qb->andWhere('r.disabled = FALSE');
        }

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

        return $qb;
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findOne(): ?object
    {
        $qb = $this->createQueryBuilder('r')
            ->orderBy('r.createdAt', 'DESC')
            ->setMaxResults(1);

        if ($this->isSoftDeletable()) {
            $qb->andWhere('r.deletedAt IS NULL');
        }

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

    private function isDisableable(): bool
    {
        return \in_array(HasDisable::class, $this->entity->getInterfaceNames(), true);
    }

    private function isSoftDeletable(): bool
    {
        return \in_array(IsSoftDeletable::class, $this->entity->getInterfaceNames(), true);
    }

    public function findBySlug($slug)
    {
        if (!\in_array(TranslatableInterface::class, $this->entity->getInterfaceNames(), true)) {
            return null;
        }
        $qb = $this->createQueryBuilder('r')
            ->innerJoin('r.translations', 'tr')
            ->andWhere('tr.slug = :slug')
            ->setParameter('slug', $slug)
            ->setMaxResults(1);

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

    #[Required]
    public function setPaginator(PaginatorInterface $paginator): void
    {
        $this->paginator = $paginator;
    }

    protected function pagination($query, int $page, int $limit = 5): PaginationInterface
    {
        return $this->paginator->paginate(
            $query, /* query NOT result */
            $page, /* page number */
            $limit /* limit per page */
        );
    }
}
