<?php
/**
 * This file is part of the DATAtourisme project.
 *
 *  @author Conjecto <contact@conjecto.com>
 *
 * For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
 */

namespace Datatourisme\Api\Schema;

use Datatourisme\Api\Exception\InvalidArgumentException;
use Datatourisme\Api\Exception\RuntimeException;
use Datatourisme\Api\Schema\Compiler\SchemaCompiler;

class SchemaLoader
{
    /**
     * @var SchemaCompiler
     */
    private $compiler;

    /**
     * @var \Twig_CacheInterface
     */
    private $cache;

    /**
     * SchemaBuilder constructor.
     *
     * @param SchemaCompiler $compiler
     *
     * @throws \Exception
     */
    public function __construct(SchemaCompiler $compiler)
    {
        $this->setCompiler($compiler);
        $this->setCache(sys_get_temp_dir().'/datatourisme'.md5(__DIR__));
    }

    /**
     * @param SchemaCompiler $compiler
     */
    public function setCompiler($compiler)
    {
        $this->compiler = $compiler;
    }

    /**
     * @param \Twig_CacheInterface|string|false
     *
     * @throws InvalidArgumentException
     */
    public function setCache($cache)
    {
        if (is_string($cache)) {
            $this->cache = new \Twig_Cache_Filesystem($cache);
        } elseif (false === $cache) {
            $this->cache = new \Twig_Cache_Null();
        } elseif ($cache instanceof \Twig_CacheInterface) {
            $this->cache = $cache;
        } else {
            throw new InvalidArgumentException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.'));
        }
    }

    /**
     * SchemaBuilder constructor.
     *
     * @param $schemaFile
     *
     * @return AbstractSchema
     *
     * @throws \Exception
     */
    public function load($schemaFile)
    {
        $namespace = $this->getSchemaNamespace($schemaFile);
        $key = $this->cache->generateKey(null, $namespace);

        $cls = $namespace.'\\Schema';
        if ($this->isFresh($schemaFile, $this->cache->getTimestamp($key))) {
            $this->cache->load($key);
        } else {
            $content = $this->compiler->compile($schemaFile, $namespace);
            $this->cache->write($key, $content);
            $this->cache->load($key);
            if (!class_exists($cls, false)) {
                eval('?>'.$content);
            }
        }

        if (!class_exists($cls, false)) {
            throw new RuntimeException(sprintf('Failed to load the Schema class. Try to empty the cache ?'));
        }

        return new $cls();
    }

    /**
     * @param $filename
     *
     * @return string
     */
    public function getSchemaNamespace($filename): string
    {
        return '__SemGraphQLSchema__'.hash('sha256', $filename);
    }

    /**
     * Returns true if the file is still fresh.
     *
     * @param $file
     * @param $time
     *
     * @return bool
     */
    public function isFresh($file, $time)
    {
        return filemtime($file) <= $time;
    }
}
