<?php
namespace Newland\NeosMetaTags\Extractor;

/*
 * This file is part of the "neos-meta-tags" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\Flow\ObjectManagement\ObjectManager;
use Newland\NeosMetaTags\Domain\Model\CanonicalUrlGenerator;
use Newland\NeosMetaTags\Domain\Model\MetaData;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Property\PropertyMapper;
use Neos\Flow\Annotations as Flow;
use Newland\NeosCommon\Service\RecordService;
use Newland\NeosMetaTags\Domain\Model\Scenario;

class RequestExtractor extends BaseExtractor
{
    /**
     * @var PropertyMapper
     * @Flow\Inject()
     */
    protected $propertyMapper;
    /**
     * @var RecordService
     * @Flow\Inject()
     */
    protected $recordService;
    /**
     * @var ObjectManager
     * @Flow\Inject()
     */
    protected $objectManager;

    public function extractAll(Node $node, Scenario $scenario): MetaData
    {
        $metaData = new MetaData();

        $record = $this->getRecord($scenario);
        if ($record === null) {
            return $metaData;
        }

        $title = $this->recordService->getPropertyWithFallback($record, $scenario->getPropertyMappingFor('title'));
        $description = strip_tags($this->extractMetaDescription($scenario, $record));
        $image = $this->recordService->getPropertyWithFallback($record, $scenario->getPropertyMappingFor('image'));

        $metaData->setTitle($this->appendWebsite($title, $node));
        $metaData->setDescription($description);
        $metaData->setUrl($scenario->getUrl());

        $metaData->setOgType($scenario->getOgType());
        $metaData->setOgTitle($title);
        $metaData->setOgDescription($description);
        $metaData->setOgThumbnail($this->thumbnailService->getThumbnailDataFromExternalImage($image));
        $metaData->setOgImageAltText($title);

        $metaData->setTwitterType(MetaData::TWITTER_CARD_TYPE_DEFAULT);
        $metaData->setTwitterTitle($title);
        $metaData->setTwitterDescription($description);
        $metaData->setTwitterImage($image);
        $metaData->setTwitterCreator($this->twitterHandle);

        if ($scenario->getCanonicalUrlGenerator()) {
            $metaData->setCanonicalUrl(
                $this->generateCanonicalUrl($scenario, $record)
            );
        }

        return $metaData;
    }

    public function extractTitle(Node $node, Scenario $scenario, bool $appendWebsite = false): ?string
    {
        $record = $this->getRecord($scenario);
        if ($record === null) {
            return null;
        }

        $recordTitle = $this->recordService->getPropertyWithFallback(
            $record,
            $scenario->getPropertyMappingFor('title')
        );

        if ($appendWebsite) {
            $recordTitle = $this->appendWebsite($recordTitle, $node);
        }

        return $recordTitle;
    }

    public function extractDescription(Scenario $scenario): ?string
    {
        $record = $this->getRecord($scenario);
        if ($record === null) {
            return null;
        }

        $description = $this->extractMetaDescription($scenario, $record);

        $metaData = new MetaData();
        $metaData->setDescription($description);

        return $metaData->getDescriptionTrimmed();
    }

    protected function getRecord(Scenario $scenario): ?object
    {
        try {
            return $this->propertyMapper->convert(
                $scenario->getRecordIdentifier(),
                $scenario->getObjectType()
            );
        } catch (\Exception $e) {
            return null;
        }
    }

    /** @param mixed $record */
    private function generateCanonicalUrl(Scenario $scenario, $record): ?string
    {
        $generatorClass = $scenario->getCanonicalUrlGenerator();
        if (!$generatorClass) {
            return null;
        }
        $generatorInstance = $this->objectManager->get($generatorClass);
        if (!($generatorInstance instanceof CanonicalUrlGenerator)) {
            throw new InvalidConfigurationException(
                sprintf(
                    'Configured canonical url generator must implement %s but %s does not.',
                    CanonicalUrlGenerator::class,
                    get_class($generatorInstance)
                )
            );
        }

        return $generatorInstance->generate($scenario, $record);
    }

    /** @param mixed $record */
    public function extractMetaDescription(Scenario $scenario, $record): string
    {
        return $this->recordService->getPropertyWithFallback($record, $scenario->getPropertyMappingFor('description'));
    }
}
