<?php 
/** 
 * Copyright Blackbit digital Commerce GmbH <info@blackbit.de> 
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
 */ 
 
namespace Blackbit\DataDirectorBundle\EventListener; 
 
use Blackbit\DataDirectorBundle\Controller\ImportController; 
use Blackbit\DataDirectorBundle\lib\Pim\Import\CallbackFunction; 
use Blackbit\DataDirectorBundle\lib\Pim\Item\ImporterInterface; 
use Blackbit\DataDirectorBundle\lib\Pim\Item\ItemMoldBuilder; 
use Blackbit\DataDirectorBundle\lib\Pim\RawData\Importer; 
use Blackbit\DataDirectorBundle\lib\Pim\Serializer; 
use Blackbit\DataDirectorBundle\model\Dataport; 
use Blackbit\DataDirectorBundle\model\DataportResource; 
use Blackbit\DataDirectorBundle\model\Fieldmapping; 
use Blackbit\DataDirectorBundle\model\ImportStatus; 
use Blackbit\DataDirectorBundle\model\PimcoreDbRepository; 
use Blackbit\DataDirectorBundle\model\Queue; 
use Blackbit\DataDirectorBundle\model\RawItem; 
use Blackbit\DataDirectorBundle\model\RawItemData; 
use Blackbit\DataDirectorBundle\Tools\Installer; 
use InvalidArgumentException; 
use Pimcore\Db; 
use Pimcore\Event\Model\AssetEvent; 
use Pimcore\Event\Model\ElementEventInterface; 
use Pimcore\Logger; 
use Pimcore\Model\AbstractModel; 
use Pimcore\Model\Asset; 
use Pimcore\Model\DataObject\AbstractObject; 
use Pimcore\Model\DataObject\Concrete; 
use Pimcore\Model\Element\DirtyIndicatorInterface; 
use Pimcore\Model\Element\ElementInterface; 
use Pimcore\Model\Element\Service; 
use Pimcore\Model\User; 
use Pimcore\Model\Version; 
use Pimcore\Tool; 
use Blackbit\DataDirectorBundle\lib\Pim\Cli; 
use Psr\Log\LoggerAwareInterface; 
use Psr\Log\LoggerAwareTrait; 
use Psr\Log\LoggerInterface; 
use Symfony\Component\HttpFoundation\Request; 
 
class AutomaticImportListener implements LoggerAwareInterface 
{ 
    use LoggerAwareTrait; 
 
    private static $processCommandRegistered = []; 
 
    /** @var ItemMoldBuilder */ 
    private $itemMoldBuilder; 
 
    public function __construct(ItemMoldBuilder $itemMoldBuilder) 
    { 
        $this->itemMoldBuilder = $itemMoldBuilder; 
    } 
 
    public function startImports(ElementEventInterface $e) { 
        if(\method_exists($e, 'getArgument')) { 
            try { 
                $saveVersionOnly = $e->getArgument('saveVersionOnly'); 
                if($saveVersionOnly) { 
                    return; 
                } 
            } catch(\InvalidArgumentException $exception) { 
            } 
        } 
 
        $object = $e->getElement(); 
 
        $user = Tool\Admin::getCurrentUser(); 
        if($user instanceof User) { 
            PimcoreDbRepository::getInstance()->execute('UPDATE edit_lock SET date=? WHERE cid=? AND ctype=? AND userId=?', [time(), $object->getId(), Service::getElementType($object), $user->getId()]); 
        } 
 
        $dataports = Dataport::getInstance(); 
        $dataportResourceRepository = DataportResource::getInstance(); 
 
        $queue = Queue::getInstance(); 
 
        foreach($dataports->find([], 'name') as $dataport) { 
            $originalLocale = \Pimcore::getContainer()->get('pimcore.locale')->getLocale(); 
            try { 
                $sourceConfig = unserialize($dataport['sourceconfig'], ['allowed_classes' => false]); 
                $targetConfig = unserialize($dataport['targetconfig'], ['allowed_classes' => false]); 
 
                if(!empty($sourceConfig['autoImport'])) { 
                    $parser = $dataports->getParser($dataport['id']); 
                    if(method_exists($parser, 'disableLoggingNotFoundImportResource')) { 
                        $parser->disableLoggingNotFoundImportResource(); 
                    } 
 
                    if(\method_exists($parser, 'setSourceFile') && \method_exists($parser, 'getFileConditionFromObject')) { 
                        if (empty($targetConfig['itemClass'])) { 
                            // Export 
                            if (empty($sourceConfig['incrementalExport'])) { 
                                $dataportResources = $dataportResourceRepository->find(['dataportId = ?' => $dataport['id']]); 
                            } else { 
                                $dataportResources = [ 
                                    $dataportResourceRepository->create( 
                                        [ 
                                            'dataportId' => $dataport['id'], 
                                            'resource' => \json_encode([], JSON_UNESCAPED_SLASHES) 
                                        ] 
                                    ) 
                                ]; 
                            } 
 
                            $dataportResourceIdsStillContainingObject = []; 
                            foreach ($dataportResources as $dataportResource) { 
                                try { 
                                    $resourceSettings = \json_decode($dataportResource['resource'], true); 
                                    $parser->setSourceFile($resourceSettings['file'] ?? null); 
                                    if (!empty($resourceSettings['locale'])) { 
                                        \Pimcore::getContainer()->get('pimcore.locale')->setLocale($resourceSettings['locale']); 
                                    } else { 
                                        $resourceSettings['locale'] = \Pimcore::getContainer()->get('pimcore.locale')->getLocale(); 
                                        if ($resourceSettings['locale'] === null) { 
                                            $resourceSettings['locale'] = Tool::getDefaultLanguage(); 
                                        } 
                                    } 
 
                                    $source = $parser->getFileConditionFromObject($object); 
                                    if ($source !== null) { 
                                        $dataportResourceIdsStillContainingObject[$source][$resourceSettings['locale']][] = $dataportResource['id']; 
                                    } elseif (empty($sourceConfig['incrementalExport'])) { 
                                        $source = $parser->getFileConditionFromObject($object, false); 
                                        if ($source !== null) { 
                                            $tmpSourceConfig = $sourceConfig; 
                                            if (\method_exists($parser, 'setConfig')) { 
                                                if (\method_exists($parser, 'getConfig')) { 
                                                    $tmpSourceConfig = $parser->getConfig(); 
                                                } 
                                                $tmpSourceConfig['file'] = ''; 
 
                                                $tmpSourceConfig['dataportId'] = $dataport['id']; 
                                                $parser->setConfig($tmpSourceConfig); 
                                            } 
 
                                            $parser->setSourceFile($source); 
 
                                            $keyFields = []; 
                                            foreach ($tmpSourceConfig['fields'] as $fieldIndex => $field) { 
                                                if (!empty($field['exportKey'])) { 
                                                    $keyFields[$fieldIndex] = $field; 
                                                } 
                                            } 
 
                                            $hashs = []; 
                                            if ($dataport['sourcetype'] === 'pimcore') { 
                                                $locales = Tool::getValidLanguages(); 
                                                if ($dataportResource !== null) { 
                                                    $resource = \json_decode($dataportResource['resource'], true); 
                                                    if ($resource['locale']) { 
                                                        $locales = [$resource['locale']]; 
                                                    } 
                                                } 
                                                foreach ($locales as $language) { 
                                                    \Pimcore::getContainer()->get('pimcore.locale')->setLocale($language); 
 
                                                    foreach ($parser as $rawItemData) { 
                                                        if ($rawItemData === null) { 
                                                            continue; 
                                                        } 
 
                                                        $rawItemData = array_filter( 
                                                            $rawItemData, 
                                                            static function ($fieldId) use ($keyFields) { 
                                                                return isset($keyFields[$fieldId]); 
                                                            }, 
                                                            ARRAY_FILTER_USE_KEY 
                                                        ); 
 
                                                        $hashs[] = Importer::getHash($rawItemData); 
                                                    } 
                                                } 
                                            } else { 
                                                foreach ($parser as $rawItemData) { 
                                                    if ($rawItemData === null) { 
                                                        continue; 
                                                    } 
 
                                                    $rawItemData = array_filter( 
                                                        $rawItemData, 
                                                        static function ($fieldId) use ($keyFields) { 
                                                            return isset($keyFields[$fieldId]); 
                                                        }, 
                                                        ARRAY_FILTER_USE_KEY 
                                                    ); 
 
                                                    $hashs[] = Importer::getHash($rawItemData); 
                                                } 
                                            } 
 
                                            if(count($hashs) > 0) { 
                                                $countDeletedRawItems = Db::get()->executeUpdate('DELETE FROM '.Installer::TABLE_RAWITEM.' WHERE dataport_resource_id = ? AND hash IN ("'.implode('","', $hashs).'")', [$dataportResource['id']]); 
                                                if ($countDeletedRawItems) { 
                                                    $this->queueProcessRawData($dataportResource['id'], $dataport['id']); 
                                                } 
                                            } 
                                        } 
                                    } 
                                } catch (\Throwable $e) { 
                                    if(!$e instanceof SkipTriggerAutomaticImportException) { 
                                        $error = 'Check for automatic start for dataport #'.$dataport['id'].' failed: '.(string)$e; 
                                        if($this->logger) { 
                                            $this->logger->error($error); 
                                        } else { 
                                            Logger::error($error); 
                                        } 
                                    } 
                                } 
                            } 
 
                            foreach($dataportResourceIdsStillContainingObject as $dataportResourceQuery => $dataportResourcesWithSameLocale) { 
                                foreach($dataportResourcesWithSameLocale as $locale => $dataportResourceIds) { 
                                    if (empty($sourceConfig['incrementalExport'])) { 
                                        $queue->create( 
                                            [ 
                                                'command' => 'data-director:extract '.$dataport['id'].' "'.str_replace(['"', '$', '`'], ['\\"', '\\$', '\\`'], $dataportResourceQuery).'"'.(!empty($locale) ? ' --locale='.$locale : '').' --dataport-resource-id='.implode(',', $dataportResourceIds), 
                                                'triggered_by' => 'Element '.$object->getFullPath().' (#'.$object->getId().') got saved'.(($user instanceof User) ? ' by '.$user->getUsername() : '') 
                                            ] 
                                        ); 
                                    } elseif ($dataport['sourcetype'] === 'pimcore' && preg_match('/ IN \(((\d+,?)+)\)/', $dataportResourceQuery, $match)) { 
                                        $ids = explode(',', $match[1]); 
                                        sort($ids, SORT_NUMERIC); 
                                        foreach (array_chunk($ids, 100) as $inIds) { 
                                            $queue->create( 
                                                [ 
                                                    'command' => 'data-director:complete '.$dataport['id'].' "'.str_replace([$match[0], '"', '$', '`'], [' IN ('.implode(',',$inIds).')', '\\"', '\\$', '\\`'], $dataportResourceQuery).'"', 
                                                    'triggered_by' => 'Element '.$object->getFullPath().' (#'.$object->getId().') got saved'.(($user instanceof User) ? ' by '.$user->getUsername() : '') 
                                                ] 
                                            ); 
                                        } 
                                    } else { 
                                        $queue->create([ 
                                            'command' => 'data-director:complete '.$dataport['id'].' "'.str_replace(['"', '$', '`'], ['\\"', '\\$', '\\`'], $dataportResourceQuery).'"', 
                                            'triggered_by' => 'Element '.$object->getFullPath().' (#'.$object->getId().') got saved'.(($user instanceof User) ? ' by '.$user->getUsername() : '') 
                                        ]); 
                                    } 
                                } 
                            } 
                        } else { 
                            try { 
                                // Import 
                                $source = $parser->getFileConditionFromObject($object); 
 
                                if ($source !== null) { 
                                    $queue->create([ 
                                        'command' => 'data-director:complete '.$dataport['id'].' "'.str_replace(['"', '$', '`'], ['\\"', '\\$', '\\`'], $source).'"', 
                                        'triggered_by' => 'Element '.$object->getFullPath().' (#'.$object->getId().') got saved'.(($user instanceof User) ? ' by '.$user->getUsername() : '') 
                                    ]); 
                                } 
                            } catch(SkipTriggerAutomaticImportException $e) { 
                            } 
                        } 
                    } 
                } 
            } catch (\Exception $e) { 
                if($this->logger) { 
                    $this->logger->error((string)$e); 
                } else { 
                    Logger::error((string)$e); 
                } 
            } finally { 
                \Pimcore::getContainer()->get('pimcore.locale')->setLocale($originalLocale); 
            } 
        } 
    } 
 
    public function deleteRawdata(ElementEventInterface $e) { 
        if(\method_exists($e, 'getArgument')) { 
            try { 
                $saveVersionOnly = $e->getArgument('saveVersionOnly'); 
                if($saveVersionOnly) { 
                    return; 
                } 
            } catch(\InvalidArgumentException $exception) { 
            } 
        } 
 
        $object = $e->getElement(); 
        $dataports = Dataport::getInstance(); 
        $objectType = Service::getElementType($object); 
        $commandPrefix = ' "'.Cli::getPhpCli().'" '.realpath(PIMCORE_PROJECT_ROOT.DIRECTORY_SEPARATOR.'bin'.DIRECTORY_SEPARATOR.'console'); 
 
        foreach($dataports->find([], 'name') as $dataport) { 
            $sourceConfig = unserialize($dataport['sourceconfig'], ['allowed_classes' => false]); 
            $targetConfig = unserialize($dataport['targetconfig'], ['allowed_classes' => false]); 
 
            if(!empty($sourceConfig['autoImport']) && empty($targetConfig['itemClass'])) { 
                $itemMold = $this->itemMoldBuilder->getItemMoldByClassId($sourceConfig['sourceClass']); 
                if($object instanceof $itemMold) { 
                    chdir(PIMCORE_PROJECT_ROOT); 
                    Cli::exec($commandPrefix.' data-director:delete-rawdata --dataport='.$dataport['id'].' --object-id='.$object->getId().' --object-type='.$objectType); 
                } 
            } 
        } 
    } 
 
    private function queueDeleteRawdata(ElementInterface $object, $dataportResourceId = null) { 
        $queue = Queue::getInstance(); 
 
        $objectType = Service::getElementType($object); 
 
        $queue->create(['command' => 'data-director:delete-rawdata --dataport-resource-id=' .$dataportResourceId . ' --object-id=' . $object->getId() . ' --object-type=' . $objectType, 'triggered_by' => 'Item '.$object->getFullPath().' does not fulfill the dataport\'s SQL condition anymore']); 
    } 
 
    private function queueProcessRawData($dataportResourceId, $dataportId) { 
        if (!isset(self::$processCommandRegistered[$dataportResourceId])) { 
            self::$processCommandRegistered[$dataportResourceId] = true; 
 
            $queue = Queue::getInstance(); 
            $cmd = 'data-director:process ' . $dataportId . ' --dataport-resource-id=' . $dataportResourceId; 
            register_shutdown_function( 
                static function () use ($queue, $cmd) { 
                    $queue->create(['command' => $cmd, 'triggered_by' => 'An item got deleted from raw data -> new result document has to be generated']); 
                } 
            ); 
        } 
    } 
}