<?php 

namespace NPS\Repository;

use Doctrine\ORM\EntityManagerInterface;
use NPS\Entity\KnowledgeBaseEntity;
use NPS\Factory\KnowledgeBaseFactory;


/**
 * Knowledge base repository
 * @author Lukas Korinek
 */
final class KnowledgeBaseRepository implements IObjectRepository
{

    /**
     * @var EntityManagerInterface $em
     */
    private $em;

    /**
     * @var array $bases Contains cached bases
     */
    private $bases;

    /**
     * @var KnowledgeBaseFactory $factory
     */
    private $factory;


    public function __construct(EntityManagerInterface $em, KnowledgeBaseFactory $factory)
    {
        $this->em = $em;
        $this->bases = null;
        $this->factory = $factory;
    }

    /**
     * @inheritdoc
     */
    public function getById($id)
    {
        $this->loadCache();

        return array_key_exists($id, $this->bases) ? $this->bases[$id] : null;
    }

    /**
     * Gets a knowledge base by its path
     * @param string $path 
     * @return KnowledgeBase|null
     */
    public function getByPath($path)
    {
        $this->loadCache();
        foreach($this->bases as $base)
        {
            if($base->getPath() == $path)
            {
                return $base;
            }
        }

        return null;
    }

    /**
     * Gets a knowledge base by a name and version
     * @param string $name 
     * @param \DateTime|null $version
     * @return KnowledgeBase|null
     */
    public function getKnowledgeBaseByNameAndVersion($name, \DateTime $version)
    {
        $this->loadCache();
        
        foreach($this->bases as $base)
        {
            if($base->getName() == $name && $base->getVersion()->format('Ymd') == $version->format('Ymd'))
            {
                return $base;
            }   
        }

        return null;
    }

    /**
     * Gets knowledge bases by their name
     * @param string $name 
     * @return array
     */
    public function getKnowledgeBasesByName($name)
    {
        $this->loadCache();

        return array_filter($this->bases, function($base) use ($name)
        {
            return $base->getName() == $name;
        });
    }

    /**
     * Gets knowledge bases by their name
     * @param int $bindId 
     * @return array
     */
    public function getByBindId($bindId)
    {
        $this->loadCache();

        return array_filter($this->bases, function($base) use ($bindId)
        {
            return $base->getKnowledgeBaseBindId() == $bindId;
        });
    }

    /**
     * Gets all available bases
     * @return array
     */
    public function getAll()
    {
        $this->loadCache();

        return array_values($this->bases);
    }

    /**
     * Gets a knowledge base bind id for a new knowledge base
     * @return int
     */
    public function getNewKnowledgeBaseBindId()
    {
        $statement = $this->em->getConnection()->prepare("SELECT MAX(bind_id) AS 'max' FROM knowledge_base");
        $statement->execute();

        // Always contains at least one row with a zero, even when no bind id is present yet
        return $statement->fetch()['max'] + 1;
    }

    /**
     * Gets a bind id by a knowledge base name
     * @param string $name
     * @return int
     */
    public function getBindIdByName($name)
    {
        $this->loadCache();

        foreach($this->bases as $base)
        {
            if($base->getName() == $name)
            {
                return $base->getKnowledgeBaseBindId();
            }
        }

        return $this->getNewKnowledgeBaseBindId();
    }

    /**
     * Validates an int to be a valid existing knowledge base bind id
     * @param int $bindId
     * @return bool 
     */
    public function validateBindId($bindId)
    {
        $this->loadCache();

        foreach($this->bases as $base)
        {
            if($base->getKnowledgeBaseBindId() == $bindId)
            {
                return true;
            }
        }

        return false;
    }

    //////////////////////////////////////////////////////////////////////

    /**
     * Clears the cache of this repository
     */
    public function clearCache()
    {
        $this->bases = null;
    }

    /**
     * Loads existing knowledge bases from either database
     */
    private function loadCache()
    {
        if(!$this->bases)
        {
            $bases = $this->em->getRepository(KnowledgeBaseEntity::class)->findBy([], ['id' => 'desc']);

            $this->bases = [];
            foreach($bases as $base)
            {
                $this->bases[$base->getId()] = $this->factory->make($base);
            }
        }
    }
}

