<?php

namespace Comitium5\DesignerBundle\Manager;

use Comitium5\DesignerBundle\Factory\AbstractFactory;
use Comitium5\DesignerBundle\Helper\Utils;
use Comitium5\DesignerBundle\Model\Interfaces\AbstractManagerInterface;
use Comitium5\DesignerBundle\Model\Interfaces\DateTimeInterface;
use ComitiumSuite\Components\Language\Model\Interfaces\LangCollectionInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Class AbstractManager
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Manager
 */
class AbstractManager implements AbstractManagerInterface
{
    /**
     * @var ObjectRepository
     */
    protected $repository;

    /**
     * @var AbstractFactory
     */
    protected $factory;

    /**
     * @var ObjectManager
     */
    protected $objectManager;

    /**
     * @var EventDispatcherInterface
     */
    protected $eventDispatcher;

    /**
     * Property to compatibility with current Manager.
     *
     * @var
     */
    protected $workingClass;

    /**
     * @var
     */
    protected $environment;

    /**
     * @return ObjectRepository
     */
    public function getRepository()
    {
        return $this->repository;
    }

    /**
     * @param ObjectRepository $repository
     *
     * @return \ComitiumSuite\Components\Core\Manager\Abstracts\AbstractManager
     */
    public function setRepository(ObjectRepository $repository)
    {
        $this->repository = $repository;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getFactory()
    {
        return $this->factory;
    }

    /**
     * @param mixed $factory
     *
     * @return $this
     */
    public function setFactory(AbstractFactory $factory)
    {
        $this->factory = $factory;

        return $this;
    }

    /**
     * @return ObjectManager
     */
    public function getObjectManager()
    {
        return $this->objectManager;
    }

    /**
     * @param EntityManager $objectManager
     *
     * @return $this
     */
    public function setObjectManager(EntityManager $objectManager)
    {
        $this->objectManager = $objectManager;

        return $this;
    }

    /**
     * Method to compatibility with current Manager.
     *
     * @return mixed
     */
    public function getWorkingClass()
    {
        return $this->factory->getEntityNamespace();
    }

    /**
     * @param $workingClass
     *
     * @return $this
     */
    public function setWorkingClass($workingClass)
    {
        $this->factory->setEntityNamespace($workingClass);

        return $this;
    }

    /**
     * @return EventDispatcherInterface|null
     */
    public function getEventDispatcher()
    {
        return $this->eventDispatcher;
    }

    /**
     * @param EventDispatcherInterface|null $eventDispatcher
     *
     * @return $this
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher = null)
    {
        $this->eventDispatcher = $eventDispatcher;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getEnvironment()
    {
        return $this->environment;
    }

    /**
     * @param $environment
     *
     * @return $this
     */
    public function setEnvironment($environment)
    {
        $this->environment = $environment;

        return $this;
    }

    /**
     * @param $data
     * @param bool $update
     *
     * @return array|mixed|void
     */
    public function save($data, $update = false)
    {
        if (!$data) {
            return;
        }

        if (is_array($data)) {
            foreach ($data as $entity) {
                $this
                    ->objectManager
                    ->persist($entity);
            }
        } else {
            $this
                ->objectManager
                ->persist($data);
        }

        $this->objectManager->flush();

//        if ($this->eventDispatcher) {
//
//            $locale = null;
//            $entity = is_array($data) ? $data[0] : $data;
//
//            if ($entity instanceof LangCollectionInterface) {
//                $locale = $entity->getDefaultLang()->getLocale();
//            }
//
//            $eventName = $update === false ? EntityEvent::EVENT_SAVE : EntityEvent::EVENT_UPDATE;
//            $this->eventDispatcher->dispatch($eventName, new EntityEvent($data, $this, $locale));
//
//            // Dispatch events by entity
//            $eventName  = $eventName.'.'.Utils::getEntityNameType($entity);
//
//            $this->eventDispatcher->dispatch($eventName, new EntityEvent($data, $this, $locale));
//        }

        return $data;
    }

    /**
     * @param $data
     *
     * @return mixed
     * @throws \Exception
     */
    public function update($data)
    {
        if (is_array($data)) {
            foreach ($data as $entity) {
                $this->setUpdatedAt($entity);
            }
        } else {
            $this->setUpdatedAt($data);
        }

        return $this->save($data, true);
    }

    /**
     * @param $data
     *
     * @return mixed
     */
    public function remove($data)
    {
        if (!$data) {
            return;
        }

        if (is_array($data)) {
            $dataClone = [];

            foreach ($data as $entity) {
                // This is required to avoid doctrine schedule exceptions
                $dataClone[] = clone($entity);

                $this
                    ->objectManager
                    ->remove($entity);
            }
        } else {
            // This is required to avoid doctrine schedule exceptions
            $dataClone = clone($data);

            $this
                ->objectManager
                ->remove($data);
        }

        $this
            ->objectManager
            ->flush($data);

//        if ($this->eventDispatcher && $this->environment !== "test") {
//            $this->eventDispatcher->dispatch(EntityEvent::EVENT_REMOVE, new EntityEvent($dataClone, $this));
//
//            // Dispatch events by entity
//            $entity = is_array($data) ? $data[0] : $data;
//            $eventName  = EntityEvent::EVENT_REMOVE.'.'.Utils::getEntityNameType($entity);
//            $this->eventDispatcher->dispatch($eventName, new EntityEvent($data, $this));
//        }

        return $data;
    }

    /**
     * @param $id
     *
     * @return mixed
     */
    public function find($id)
    {
        $entity = $this->repository->find($id);

        if (!$entity) {
            throw new NotFoundHttpException(sprintf('Entity %s not found', $this->getWorkingClass()));
        }

        return $entity;
    }

    /**
     * @param $class
     * @param $data
     *
     * @return null
     */
    public function getReference($class, $data)
    {
        $entity   = null;
        $entityId = 0;

        if (is_array($data)) {
            $entityId = 0;
            if (!empty($data['id'])) {
                $entityId = intval($data['id']);
            }
        } elseif (is_numeric($data)) {
            $entityId = intval($data);
        }
        if ($entityId > 0) {
            $entity = $this->objectManager->getReference($class, $entityId);
        }

        return $entity;
    }

    /**
     * @param DateTimeInterface|array $data
     * @param string $time
     *
     * @return mixed
     * @throws \Exception
     */
    private function setUpdatedAt($data, $time = 'now')
    {
        if ($data instanceof DateTimeInterface) {
            $data->setUpdatedAt(new \DateTime($time));
        }
    }
}
