<?php

namespace KupShop\ElninoBundle\Util;

use KupShop\ElninoBundle\Translations\AbraModelsTranslation;
use KupShop\ElninoBundle\Translations\AbraSeriesTranslation;
use KupShop\I18nBundle\Translations\ProducersTranslation;
use KupShop\I18nBundle\Translations\ProductsTranslation;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\System\PathFinder;
use Query\Operator;

class RichContentUtil
{
    /** @var Pathfinder */
    protected $pathFinder;

    /** @var array */
    protected $typeToTranslation = [
        'products' => ProductsTranslation::class,
        'abra_models' => AbraModelsTranslation::class,
        'abra_series' => AbraSeriesTranslation::class,
        'producers' => ProducersTranslation::class,
    ];

    protected function getPathFinder(): PathFinder
    {
        if (!isset($this->pathFinder)) {
            $this->pathFinder = PathFinder::getService();
        }

        return $this->pathFinder;
    }

    public function handleSync(string $type, $objectID, $abraRichContentID, $languageID)
    {
        if (empty($abraRichContentID)) {
            // To hide or not to hide
            $translation = ServiceContainer::getService($this->typeToTranslation[$type]);
            $result = sqlQueryBuilder()->select($translation->getTableAlias().'.id_richcontent')
                ->from($type, $translation->getTableAlias())
                ->where(\Query\Operator::equals([$translation->getTableAlias().'.'.$translation->getKeyColumn() => $objectID]))
                ->andWhere(\Query\Translation::coalesceTranslatedFields($this->typeToTranslation[$type], ['id_richcontent']))
                ->execute()->fetch();
            if (!$result || empty($result['id_richcontent'])) {
                return;
            }
            $rc = sqlQueryBuilder()->select('id')->from('abra_richcontents')
                ->where('id = :id AND id_abra IS NOT NULL')->setparameter('id', $result['id_richcontent'])
                ->execute()->fetch();
            if (!$rc) {
                return; // preserve manually uploaded rich contents (that have null id_abra)
            }
            $rcID = null; // hide rich content
        } else {
            $rcID = $this->getRichContentID($abraRichContentID, $languageID);
        }
        if (!empty($rcID)) {
            $rcContent = $this->getRichContent($rcID);
            if (empty(trim(strip_tags($rcContent))) && mb_strpos($rcContent, '<img') === false && mb_strpos($rcContent, '<video') === false) {
                $rcID = null; // empty content without img/video => do not display rich content #9693
            }
        }
        if (findModule(\Modules::TRANSLATIONS)) {
            $translation_service = ServiceContainer::getService($this->typeToTranslation[$type]);
            $translation_service->saveSingleObject($languageID, $objectID, ['id_richcontent' => $rcID]);
        } else {
            if ($type === 'abra_models' || $type === 'abra_series') {
                sqlQueryBuilder()->update($type)->directValues(['id_richcontent' => $rcID])->where(Operator::equals(['id_abra' => $objectID]))->execute();
            } else {
                sqlQueryBuilder()->update($type)->directValues(['id_richcontent' => $rcID])->where(Operator::equals(['id' => $objectID]))->execute();
            }
        }
    }

    protected function getRichContentID($abraRichContentID, $languageID): int
    {
        if (!isset($abraRichContentID)) {
            return false;
        }
        $pathFinder = $this->getPathFinder();
        $rc = sqlQueryBuilder()->select('id')->from('abra_richcontents')->where(Operator::equals(['id_abra' => $abraRichContentID]))
            ->setMaxResults(1)->execute()->fetch();
        if ($rc) {
            return $rc['id'];
        }

        // try a shared storage first
        $dirRelative = 'photos/shared/rich_content/'.mb_substr('00'.$abraRichContentID, -2).'/'.$abraRichContentID;
        $dir = $pathFinder->getDataDir().$dirRelative;
        if (file_exists($dir)) {
            sqlQueryBuilder()->insert('abra_richcontents')->directValues([
                'id_abra' => $abraRichContentID,
                'id_language' => $languageID,
                'path' => $dir,
            ])->execute();

            return sqlInsertId();
        }

        // download if not present in the shared storage
        $tmpDir = $pathFinder->getTmpDir().'rich_content_tmp/';
        @mkdir($tmpDir);
        $tmpPath = tempnam($tmpDir, 'rc_');
        if ($this->downloadFile($abraRichContentID, $tmpPath)) {
            return $this->saveArchive($tmpPath, $languageID, $abraRichContentID);
        }
        throw new \Exception("Rich content with abra ID '{$abraRichContentID}' was not found and download failed.");
    }

    protected function downloadFile($abraRichContentID, $tmpPath)
    {
        $archiveURL = 'https://server23.elnino.cz:81/rich-content?id='.urlencode($abraRichContentID);
        $fp = fopen($tmpPath, 'w+');
        $options = [
            CURLOPT_FILE => $fp,
            CURLOPT_TIMEOUT => 900, // 15 min timeout
            CURLOPT_URL => $archiveURL,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_SSL_VERIFYPEER => false,

            CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
            CURLOPT_USERPWD => 'cpd:cepede',
        ];
        $ch = curl_init();
        curl_setopt_array($ch, $options);
        curl_exec($ch);
        curl_close($ch);
        fclose($fp);

        if (filesize($tmpPath) < 1) {
            return false;
        }

        return true;
    }

    protected function saveArchive($tmpPath, $languageID, $abraRichContentID = null): int
    {
        return sqlGetConnection()->transactional(function () use ($tmpPath, $languageID, $abraRichContentID) {
            sqlQueryBuilder()->insert('abra_richcontents')->directValues(['id_abra' => $abraRichContentID, 'id_language' => $languageID])->execute();
            $rcID = sqlInsertId();

            $zip = new \ZipArchive();
            $zip->open($tmpPath);

            if (isset($abraRichContentID)) {
                // shared storage (subdir with last two digits from abra_richcontents.id_abra)
                $dirRelative = 'photos/shared/rich_content/'.mb_substr('00'.$abraRichContentID, -2).'/'.$abraRichContentID;
            } else {
                // subdir with last two digits from abra_richcontents.id
                $dirRelative = 'rich_content/'.mb_substr('00'.$rcID, -2).'/'.$rcID;
            }
            $dir = $this->getPathFinder()->getDataDir().$dirRelative;
            @mkdir($dir, 0777, true);

            $zip->extractTo($dir.'/');

            // fix paths to assets/css
            $htmlFile = $dir.'/index.html';
            $content = file_get_contents($htmlFile);
            $content = str_replace('href="css/', 'href="/data/'.$dirRelative.'/css/', $content);
            // src and poster (video preview)
            $content = str_replace('="assets/', '="/data/'.$dirRelative.'/assets/', $content);
            $content = str_replace('="js/', '="/data/'.$dirRelative.'/js/', $content);
            file_put_contents($htmlFile, $content);

            sqlQueryBuilder()->update('abra_richcontents')->directValues(['path' => $dir])
                ->where(Operator::equals(['id' => $rcID]))->execute();
            @unlink($tmpPath);

            return $rcID;
        });
    }

    public function getRichContent(int $id): ?string
    {
        $rc = sqlQueryBuilder()->select('*')->from('abra_richcontents')
            ->where(\Query\Operator::equals(['id' => $id]))
            ->execute()->fetch();

        if (!empty($rc['path'])) {
            $filePath = $rc['path'].'/index.html';
            if (file_exists($filePath)) {
                return file_get_contents($filePath);
            }
        }

        return null;
    }
}
