<?php
namespace Omeka\Api\Adapter;

use Doctrine\ORM\QueryBuilder;
use Omeka\Api\Request;
use Omeka\Entity\EntityInterface;
use Omeka\Entity\Vocabulary;
use Omeka\Stdlib\ErrorStore;
use Omeka\Stdlib\Message;

class PropertyAdapter extends AbstractEntityAdapter
{
    protected $sortFields = [
        'id' => 'id',
        'local_name' => 'localName',
        'label' => 'label',
        'comment' => 'comment',
    ];

    protected $scalarFields = [
        'id' => 'id',
        'local_name' => 'localName',
        'label' => 'label',
        'comment' => 'comment',
        'owner' => 'owner',
        'vocabulary' => 'vocabulary',
    ];

    public function getResourceName()
    {
        return 'properties';
    }

    public function getRepresentationClass()
    {
        return \Omeka\Api\Representation\PropertyRepresentation::class;
    }

    public function getEntityClass()
    {
        return \Omeka\Entity\Property::class;
    }

    public function sortQuery(QueryBuilder $qb, array $query)
    {
        if (is_string($query['sort_by'])) {
            if ('item_count' == $query['sort_by']) {
                $valuesAlias = $qb->createAlias();
                $resourceAlias = $qb->createAlias();
                $countAlias = $qb->createAlias();
                $qb->addSelect("COUNT($valuesAlias.id) HIDDEN $countAlias")
                    ->leftJoin("omeka_root.values", $valuesAlias)
                    ->leftJoin(
                        "$valuesAlias.resource", $resourceAlias,
                        'WITH', "$resourceAlias INSTANCE OF Omeka\Entity\Item"
                    )->addGroupBy("omeka_root.id")
                    ->addOrderBy($countAlias, $query['sort_order']);
            } else {
                parent::sortQuery($qb, $query);
            }
        }
    }

    public function hydrate(Request $request, EntityInterface $entity,
        ErrorStore $errorStore
    ) {
        $data = $request->getContent();
        $this->hydrateOwner($request, $entity);

        if ($this->shouldHydrate($request, 'o:local_name')) {
            $entity->setLocalName($request->getValue('o:local_name'));
        }
        if ($this->shouldHydrate($request, 'o:label')) {
            $entity->setLabel($request->getValue('o:label'));
        }
        if ($this->shouldHydrate($request, 'o:comment')) {
            $entity->setComment($request->getValue('o:comment'));
        }
    }

    public function buildQuery(QueryBuilder $qb, array $query)
    {
        if (isset($query['owner_id']) && is_numeric($query['owner_id'])) {
            $userAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.owner',
                $userAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$userAlias.id",
                $qb->createNamedParameter($query['owner_id']))
            );
        }
        if (isset($query['vocabulary_id']) && is_numeric($query['vocabulary_id'])) {
            $vocabularyAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.vocabulary',
                $vocabularyAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$vocabularyAlias.id",
                $qb->createNamedParameter($query['vocabulary_id']))
            );
        }
        if (isset($query['vocabulary_namespace_uri'])) {
            $vocabularyAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.vocabulary',
                $vocabularyAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$vocabularyAlias.namespaceUri",
                $qb->createNamedParameter($query['vocabulary_namespace_uri']))
            );
        }
        if (isset($query['vocabulary_prefix'])) {
            $vocabularyAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.vocabulary',
                $vocabularyAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$vocabularyAlias.prefix",
                $qb->createNamedParameter($query['vocabulary_prefix']))
            );
        }
        if (isset($query['local_name'])) {
            $qb->andWhere($qb->expr()->eq(
                "omeka_root.localName",
                $qb->createNamedParameter($query['local_name']))
            );
        }
        if (isset($query['term']) && $this->isTerm($query['term'])) {
            [$prefix, $localName] = explode(':', $query['term']);
            $vocabularyAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.vocabulary',
                $vocabularyAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$vocabularyAlias.prefix",
                $qb->createNamedParameter($prefix))
            );
            $qb->andWhere($qb->expr()->eq(
                "omeka_root.localName",
                $qb->createNamedParameter($localName))
            );
        }
        //limit results to properties used by resources
        if (!empty($query['used'])) {
            $valuesAlias = $qb->createAlias();
            $qb->innerJoin(
                'omeka_root.values',
                $valuesAlias
            );
        }
        //limit results to properties used by items in the site
        if (isset($query['site_id']) && is_numeric($query['site_id'])) {
            $siteAlias = $qb->createAlias();
            $itemAlias = $qb->createAlias();
            $valuesAlias = $qb->createAlias();
            $subquery = $this->createQueryBuilder()
                ->select("IDENTITY($valuesAlias.property)")
                ->from('Omeka\Entity\Value', $valuesAlias)
                ->join('Omeka\Entity\Site', $siteAlias)
                ->join(
                    "$siteAlias.items",
                    $itemAlias,
                    'WITH',
                    "$itemAlias.id = $valuesAlias.resource")
                ->andWhere($qb->expr()->eq("$siteAlias.id",
                    $qb->createNamedParameter($query['site_id'])));
            $qb->andWhere($qb->expr()->in('omeka_root.id', $subquery->getDQL()));
        }

        if (isset($query['site_id_item_sets']) && is_numeric($query['site_id_item_sets'])) {
            $siteAlias = $qb->createAlias();
            $itemSetAlias = $qb->createAlias();
            $valuesAlias = $qb->createAlias();
            $subquery = $this->createQueryBuilder()
                ->select("IDENTITY($valuesAlias.property)")
                ->from('Omeka\Entity\Value', $valuesAlias)
                ->join('Omeka\Entity\Site', $siteAlias)
                ->join(
                    "$siteAlias.siteItemSets",
                    $itemSetAlias,
                    "WITH",
                    "$itemSetAlias.itemSet = $valuesAlias.resource")
                ->andWhere($qb->expr()->eq(
                    "$siteAlias.id",
                    $qb->createNamedParameter($query['site_id_item_sets'])
                ));
            $qb->andWhere($qb->expr()->in('omeka_root.id', $subquery->getDQL()));
        }
    }

    public function validateEntity(EntityInterface $entity, ErrorStore $errorStore)
    {
        // Validate local name
        if (false == $entity->getLocalName()) {
            $errorStore->addError('o:local_name', 'The local name cannot be empty.'); // @translate
        }

        // Validate label
        if (false == $entity->getLabel()) {
            $errorStore->addError('o:label', 'The label cannot be empty.'); // @translate
        }

        // Validate vocabulary
        if ($entity->getVocabulary() instanceof Vocabulary) {
            if ($entity->getVocabulary()->getId()) {
                // Vocabulary is persistent. Check for unique local name.
                $criteria = [
                    'vocabulary' => $entity->getVocabulary(),
                    'localName' => $entity->getLocalName(),
                ];
                if (!$this->isUnique($entity, $criteria)) {
                    $errorStore->addError('o:local_name', new Message(
                        'The local name "%s" is already taken.', // @translate
                        $entity->getLocalName()
                    ));
                }
            }
        } else {
            $errorStore->addError('o:vocabulary', 'A vocabulary must be set.'); // @translate
        }
    }
}
