<?php

namespace App\Repository;

use App\Entity\Manga;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\Persistence\ManagerRegistry;

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

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

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

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

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

    public function findByTitle(string $title): array
    {
        return $this->createQueryBuilder('m')
            ->andWhere('m.title LIKE :title')
            ->setParameter('title', "%$title%")
            ->getQuery()
            ->getResult();
    }

    public function findBySlug(string $slug): array
    {
        $this->getEntityManager()->getConnection()->executeStatement('CREATE EXTENSION IF NOT EXISTS fuzzystrmatch');

        $conn = $this->getEntityManager()->getConnection();

        $sql = '
                SELECT m.*, levenshtein(LOWER(m.slug), LOWER(:slug)) as distance
                FROM manga m
                WHERE levenshtein(LOWER(m.slug), LOWER(:slug)) <= :max_distance
                ORDER BY distance
                LIMIT 10
                ';

        $stmt = $conn->prepare($sql);
        $resultSet = $stmt->executeQuery([
            'slug' => $slug,
            'max_distance' => intval(ceil(strlen($slug) / 3))
        ]);

        $results = $resultSet->fetchAllAssociative();

        $ids = array_column($results, 'id');
        $entities = $this->findBy(['id' => $ids]);

        $sortedEntities = [];
        foreach ($results as $result) {
            foreach ($entities as $entity) {
                if ($entity->getId() == $result['id']) {
                    $sortedEntities[] = $entity;
                    break;
                }
            }
        }

        return $sortedEntities;
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findOneWithChapterBy(array $params): ?Manga
    {
        $query = $this->createQueryBuilder('m');
        foreach ($params as $key => $value) {
            $query->andWhere("m.$key = :$key")
                ->setParameter($key, $value);
        }
        $query->leftJoin('m.chapters', 'c')
            ->addSelect('c');

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

    public function findAllSortedAndFiltered($sort = 'title', $order = 'asc', $status = 'all')
    {
        $qb = $this->createQueryBuilder('m')
            ->orderBy('m.' . $sort, $order);

        if ($status !== 'all') {
            $qb->andWhere('m.status = :status')
                ->setParameter('status', $status);
        }

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

    //    /**
    //     * @return Manga[] Returns an array of Manga objects
    //     */
    //    public function findByExampleField($value): array
    //    {
    //        return $this->createQueryBuilder('m')
    //            ->andWhere('m.exampleField = :val')
    //            ->setParameter('val', $value)
    //            ->orderBy('m.id', 'ASC')
    //            ->setMaxResults(10)
    //            ->getQuery()
    //            ->getResult()
    //        ;
    //    }

    //    public function findOneBySomeField($value): ?Manga
    //    {
    //        return $this->createQueryBuilder('m')
    //            ->andWhere('m.exampleField = :val')
    //            ->setParameter('val', $value)
    //            ->getQuery()
    //            ->getOneOrNullResult()
    //        ;
    //    }
}
