<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\Awardee;
use Doctrine\ORM\EntityManager;
use UtilityModule\Hydrator\DoctrineObject;

class AwardeeManager
{
    const SORT_LOCALE = 'hu_HU.UTF8';
    const IMAGE_DIRECTORY = 'data/user-data/images/awardee';

    /** @var EntityManager */
    private $entityManager;

    /** @var DoctrineObject */
    private $hydrator;

    public function __construct(
        EntityManager $entityManager,
        DoctrineObject $hydrator
    ) {
        $this->entityManager = $entityManager;
        $this->hydrator = $hydrator;
    }

    /**
     * @return Awardee[]|null
     */
    public function getAwardees(): ?array
    {
        $result = $this->entityManager->getRepository(Awardee::class)->findBy([], [
            'year' => 'DESC',
            'name' => 'ASC',
        ]);
        return $this->sortAwardeesResult($result);
    }

    /**
     * @param int $year
     * @return Awardee[]
     */
    public function getAwardeesByYear(int $year): ?array
    {
        $result = $this->entityManager->getRepository(Awardee::class)->findBy([
            'year' => $year,
        ], [
            'name' => 'ASC',
        ]);
        return $this->sortAwardeesResult($result);
    }

    /**
     * @param Awardee[] $awardees
     * @return Awardee[]
     */
    private function sortAwardeesResult(array $awardees): array
    {
        setlocale(LC_COLLATE, self::SORT_LOCALE);
        usort($awardees, function (Awardee $a, Awardee $b) {
            return strcoll($a->getName(), $b->getName());
        });
        return $awardees;
    }

    /**
     * @param int $id
     * @return Awardee|null
     */
    public function getAwardee(int $id): ?Awardee
    {
        /** @var Awardee $awardee */
        $awardee = $this->entityManager->getRepository(Awardee::class)->find($id);
        return $awardee;
    }

    /**
     * @param string $slug
     * @return Awardee|null
     */
    public function getAwardeeFromSlug(string $slug): ?Awardee
    {
        /** @var Awardee $awardee */
        $awardee = $this->entityManager->getRepository(Awardee::class)->findOneBy([
           'slug' => $slug,
        ]);
        return $awardee;
    }

    /**
     * @param $data
     * @return Awardee
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function create($data): Awardee
    {
        unset($data['slug']);
        /** @var Awardee $awardee */
        $awardee = $this->hydrator->hydrate($data, new Awardee());
        $this->entityManager->persist($awardee);
        $this->entityManager->flush();
        return $awardee;
    }

    /**
     * @param int $id
     * @param $data
     * @return Awardee
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function update(int $id, $data): Awardee
    {
        unset($data['slug']);
        $awardee = $this->entityManager->getRepository(Awardee::class)->find($id);
        /** @var Awardee $awardee */
        $awardee = $this->hydrator->hydrate($data, $awardee);
        $this->entityManager->persist($awardee);
        $this->entityManager->flush();
        return $awardee;
    }

    /**
     * @param int $id
     * @return bool
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function delete(int $id): bool
    {
        if (null !== ($entity = $this->getAwardee($id))) {
            $this->entityManager->remove($entity);
            $this->entityManager->flush();
            return true;
        }
        return false;
    }

    /**
     * @param string $slug
     * @param string $type
     * @return string
     */
    public function getProfileImage(string $slug, string $type): string
    {
        /** @var Awardee $entity */
        $entity = $this->entityManager->getRepository(Awardee::class)->findOneBy([
          'slug' => $slug,
        ]);
        $fileName = $this->getImageFileName($entity->getId(), $type);
        if (file_exists($fileName)) {
            return file_get_contents($fileName);
        }
        throw new \InvalidArgumentException("$fileName not found");
    }

    /**
     * @param string $slug
     * @param string $imageData
     * @param string $type
     * @return bool
     */
    public function setProfileImage(string $slug, string $imageData, string $type): bool
    {
        /** @var Awardee $entity */
        $entity = $this->entityManager->getRepository(Awardee::class)->findOneBy([
          'slug' => $slug,
        ]);
        $this->ensureImageDirectoryExists();
        $fileName = $this->getImageFileName($entity->getId(), $type);
        return false !== file_put_contents($fileName, $imageData);
    }

    /**
     * @param string $slug
     * @param string $type
     * @return bool
     */
    public function deleteProfileImage(string $slug, string $type): bool
    {
        /** @var Awardee $entity */
        $entity = $this->entityManager->getRepository(Awardee::class)->findOneBy([
          'slug' => $slug,
        ]);
        $fileName = $this->getImageFileName($entity->getId(), $type);
        if (file_exists($fileName)) {
            return unlink($fileName);
        }
        throw new \InvalidArgumentException();
    }

    public function ensureImageDirectoryExists()
    {
        if (!is_dir(self::IMAGE_DIRECTORY)) {
            mkdir(self::IMAGE_DIRECTORY, 0755, true);
        }
    }

    private function getImageFileName(int $id, string $type): string
    {
        return sprintf('%s/%s-%s.jpg', self::IMAGE_DIRECTORY, $id, $type);
    }
}
