<?php

namespace App\Repository;

use App\Dto\SnipFilterRequest;
use App\Entity\Snip;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @extends ServiceEntityRepository<Snip>
 *
 * @method Snip|null find($id, $lockMode = null, $lockVersion = null)
 * @method Snip|null findOneBy(array $criteria, array $orderBy = null)
 * @method Snip[]    findAll()
 * @method Snip[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class SnipRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Snip::class);
    }

    public function save(Snip $entity, bool $flush = true): void
    {
        $this->getEntityManager()->persist($entity);

        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }

    public function remove(Snip $entity, bool $flush = true): void
    {
        $this->getEntityManager()->remove($entity);

        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }

    public function findByRequest(UserInterface $user, SnipFilterRequest $request): array
    {
        $qb = $this
            ->createQueryBuilder('s')
            ->where('s.createdBy = :user')
            ->setParameter('user', $user)
        ;

        $showArchived = false;
        switch ($request->visibility) {
            case SnipFilterRequest::VISIBILITY_ALL:
                break;
            case SnipFilterRequest::VISIBILITY_VISIBLE:
                $qb->andWhere('s.visible = true');
                break;
            case SnipFilterRequest::VISIBILITY_HIDDEN:
                $qb->andWhere('s.visible = false');
                break;
            case SnipFilterRequest::VISIBILITY_ARCHIVED:
                $showArchived = true;
                break;
            default:
                throw new \InvalidArgumentException('Invalid visibility option: ', $request->visibility);
        }
        $qb->andWhere('s.archived = ' . ($showArchived ? 'true' : 'false'));

        switch ($request->sort) {
            case SnipFilterRequest::SORT_NAME:
                $qb->orderBy('s.name', 'ASC');
                break;
            case SnipFilterRequest::SORT_DATE:
                $qb->orderBy('s.createdAt', 'DESC');
                break;
            default:
                throw new \InvalidArgumentException('Invalid sort option: ', $request->sort);
        }

        if ($request->tag === 'none') {
            $qb->andWhere('s.tags IS EMPTY');
        } elseif ($request->tag === 'all') {
            // No filter needed
        } else {
            $qb->innerJoin('s.tags', 't')
               ->andWhere('t.name = :tag')
               ->setParameter('tag', $request->tag)
            ;
        }

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

    public function findPublic(?User $user = null): array
    {
        $qb = $this
            ->createQueryBuilder('s')
            ->where('s.public = true')
            ->andWhere('s.visible = true')
            ->andWhere('s.archived = false')
            ->orderBy('s.createdAt', 'DESC')
        ;

        if ($user) {
            $qb->andWhere('s.createdBy != :user')
               ->setParameter('user', $user)
            ;
        }

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