<?php

namespace KupShop\CatalogBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Util\StringUtil;
use Query\Operator;

/**
 * Sections.
 *
 * @ORM\Table(name="sections", indexes={@ORM\Index(name="sections_ibfk_1", columns={"id_slider"})})
 *
 * @ORM\Entity
 */
class Section implements \ArrayAccess, \JsonSerializable
{
    private const DISPLAY_PRODUCTS_FROM_SUBSECTIONS = 2;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     *
     * @ORM\Id
     *
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var int
     *
     * @ORM\Column(name="id_block", type="integer")
     */
    private $idBlock;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=50, nullable=false)
     */
    private $name = '';

    /**
     * @var string
     *
     * @ORM\Column(name="name_short", type="string", length=50, nullable=true)
     */
    private $nameShort;

    /**
     * @var string
     *
     * @ORM\Column(name="descr", type="text", length=16777215, nullable=false)
     */
    private $descr;

    /**
     * @var string
     *
     * @ORM\Column(name="figure", type="string", nullable=false)
     */
    private $figure = 'Y';

    /**
     * @var string
     */
    private $show_in_search = 'Y';

    /**
     * @var string
     *
     * @ORM\Column(name="behaviour", type="string", nullable=false)
     */
    private $behaviour = '1';

    /**
     * @var string
     *
     * @ORM\Column(name="orderby", type="string", nullable=false)
     */
    private $orderby;

    /**
     * @var string
     *
     * @ORM\Column(name="orderdir", type="string", nullable=false)
     */
    private $orderdir = 'ASC';

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="date", nullable=false)
     */
    private $date_updated = '0000-00-00';

    /**
     * @var string
     *
     * @ORM\Column(name="list_variations", type="string", nullable=false)
     */
    private $listVariations = 'N';

    /**
     * @var string
     *
     * @ORM\Column(name="lead_figure", type="string", nullable=false)
     */
    private $leadFigure = 'N';

    /**
     * @var string
     *
     * @ORM\Column(name="lead_text", type="text", length=16777215, nullable=false)
     */
    private $leadText;

    /**
     * @var string
     *
     * @ORM\Column(name="lead_products", type="string", length=50, nullable=false)
     */
    private $leadProducts = '';

    /**
     * @var string
     *
     * @ORM\Column(name="photo", type="string", length=50, nullable=true)
     */
    private $photo;

    private $photos;

    /**
     * @var string
     *
     * @ORM\Column(name="meta_title", type="string", length=70, nullable=true)
     */
    private $metaTitle;

    /**
     * @var string
     *
     * @ORM\Column(name="meta_description", type="string", length=250, nullable=true)
     */
    private $metaDescription;

    /**
     * @var string
     *
     * @ORM\Column(name="meta_keywords", type="string", length=100, nullable=true)
     */
    private $metaKeywords;

    /**
     * @var int
     *
     * @ORM\Column(name="feed_heureka", type="integer", nullable=true)
     */
    private $feedHeureka;

    /**
     * @var int
     *
     * @ORM\Column(name="feed_heureka_sk", type="integer", nullable=true)
     */
    private $feedHeurekaSk;

    /**
     * @var int
     *
     * @ORM\Column(name="feed_google", type="integer", nullable=true)
     */
    private $feedGoogle;

    /**
     * @var int
     *
     * @ORM\Column(name="feed_glami", type="integer", nullable=true)
     */
    private $feedGlami;

    /**
     * @var array
     *
     * @ORM\Column(name="flags", type="simple_array", nullable=false)
     */
    private $flags;

    /**
     * @var int
     *
     * @ORM\Column(name="feed_seznam", type="integer", nullable=true)
     */
    private $feedSeznam;

    /**
     * @var int|null
     *
     * @ORM\Column(name="id_slider", type="integer", nullable=true)
     */
    private $idSlider;

    /**
     * @var string
     *
     * @ORM\Column(name="producers_filter", type="string", nullable=true)
     */
    private $producersFilter = 'Y';

    private ?string $producersToTitle = null;
    private ?string $producersIndexing = null;

    private ?string $labelsFilter = null;

    /**
     * @var string
     *
     * @ORM\Column(name="url", type="string", nullable=true, length=50)
     */
    private $url;

    /**
     * @var string
     *
     * @ORM\Column(name="redirect_url", type="string", nullable=true, length=50)
     */
    private $redirect_url;

    /**
     * @var string|null
     *
     * @ORM\Column(name="data", type="text", nullable=true)
     */
    private $data;

    /**
     * @var string
     *
     * @ORM\Column(name="virtual", type="text", columnDefinition="ENUM('Y', 'N')", nullable=false)
     */
    private $virtual;

    /**
     * @var int|null
     *
     * @ORM\Column(name="priority", type="integer", nullable=true)
     */
    private $priority;

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $children;

    /**
     * @var Section
     */
    private $parent;

    private ?array $parents = null;

    /**
     * @var int
     */
    private $count;

    /**
     * @var int
     */
    private $inStockCount;

    /** @var string */
    private $annotation;

    private array $sliders = [];

    private ?string $template;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->children = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setId(int $id): Section
    {
        $this->id = $id;

        return $this;
    }

    public function getIdBlock(): ?int
    {
        return $this->idBlock;
    }

    public function setIdBlock(?int $idBlock = null): Section
    {
        $this->idBlock = $idBlock;

        return $this;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): Section
    {
        $this->name = $name;

        return $this;
    }

    public function getNameShort(): ?string
    {
        return $this->nameShort;
    }

    public function setNameShort(?string $nameShort): Section
    {
        $this->nameShort = $nameShort;

        return $this;
    }

    public function getDescr()
    {
        return $this->descr;
    }

    public function setDescr(string $descr): Section
    {
        $this->descr = $descr;

        return $this;
    }

    public function getFigure(): string
    {
        return $this->figure;
    }

    public function setFigure(string $figure): Section
    {
        $this->figure = $figure;

        return $this;
    }

    public function getShowInSearch(): string
    {
        return $this->show_in_search;
    }

    public function setShowInSearch(string $show_in_search): Section
    {
        $this->show_in_search = $show_in_search;

        return $this;
    }

    public function getBehaviour(): string
    {
        return $this->behaviour;
    }

    public function setBehaviour(string $behaviour): Section
    {
        $this->behaviour = $behaviour;

        return $this;
    }

    public function getOrderby(): string
    {
        return $this->orderby;
    }

    public function setOrderby(string $orderby): Section
    {
        $this->orderby = $orderby;

        return $this;
    }

    public function getOrderdir(): string
    {
        return $this->orderdir;
    }

    public function setOrderdir(string $orderdir): Section
    {
        $this->orderdir = $orderdir;

        return $this;
    }

    public function getDateUpdated(): \DateTime
    {
        return $this->date_updated;
    }

    public function setDateUpdated(\DateTime $date): Section
    {
        $this->date_updated = $date;

        return $this;
    }

    public function getListVariations(): string
    {
        return $this->listVariations;
    }

    public function setListVariations(string $listVariations): Section
    {
        $this->listVariations = $listVariations;

        return $this;
    }

    public function getLeadFigure(): string
    {
        return $this->leadFigure;
    }

    public function setLeadFigure(string $leadFigure): Section
    {
        $this->leadFigure = $leadFigure;

        return $this;
    }

    public function getLeadText(): string
    {
        return $this->leadText;
    }

    public function setLeadText(string $leadText): Section
    {
        $this->leadText = $leadText;

        return $this;
    }

    public function getLeadProducts(): string
    {
        return $this->leadProducts;
    }

    public function setLeadProducts(string $leadProducts): Section
    {
        $this->leadProducts = $leadProducts;

        return $this;
    }

    public function getPhoto(): ?array
    {
        return $this->photo ? getImage($this->getId(), '', '', 'section', '', $this->getDateUpdated()->getTimestamp()) : null;
    }

    public function setPhoto(?string $photo = null): Section
    {
        $this->photo = $photo;

        return $this;
    }

    public function getPhotoFilename(): ?string
    {
        return !empty($this->photo) ? $this->photo : null;
    }

    /**
     * @return array|null
     */
    public function getPhotos()
    {
        $SQL = sqlQueryBuilder()->select('p.id, p.descr, p.source, p.image_2, p.date, p.data, p.date_update')
                ->from('photos', 'p')
                ->join('p', 'photos_sections_relation', 'psr', 'psr.id_photo = p.id')
                ->andWhere(Operator::equals(['psr.id_section' => $this->getId()]))
                ->orderBy('psr.position')
                ->execute();

        $photos = [];
        foreach ($SQL as $row) {
            $photos[] = getImage($row['id'], '', '', 'section', $row['descr'], strtotime($row['date_update']), $row['data']);
        }

        return $photos;
    }

    /**
     * @return $this
     */
    public function setPhotos(array $photos)
    {
        $this->photos = $photos;

        return $this;
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        if ($this->getRedirectUrl()) {
            return ltrim($this->getRedirectUrl(), '/');
        }

        if ($this->url) {
            return $this->url.'/';
        }

        return $this->getAutogeneratedUrl();
    }

    public function getFullUrl()
    {
        $url = $this->getUrl();
        if (StringUtil::isAbsoluteUrl($url)) {
            return $url;
        }

        return Config::get()['Addr']['full'].$url;
    }

    /**
     * @return string
     */
    public function getAutogeneratedUrl()
    {
        $seoTitle = StringUtil::slugify($this->getName());
        $seoUrl = '';
        foreach ($this->getParents() as $id => $parent) {
            if ($id != $this->getId()) {
                $seoUrl .= StringUtil::slugify($parent->getName()).'_k'.$id.'/';
            }
        }

        if (!empty($seoTitle)) {
            $seoUrl .= $seoTitle;
        } else {
            $seoUrl .= translate('category', 'SEO_URL');
        }

        $seoUrl .= '_k'.$this->getId().'/';

        return $seoUrl;
    }

    /**
     * @return $this
     */
    public function setUrl($url)
    {
        $this->url = $url;

        return $this;
    }

    public function getRedirectUrl(): ?string
    {
        return $this->redirect_url;
    }

    public function setRedirectUrl(?string $redirect_url): Section
    {
        $this->redirect_url = $redirect_url;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getMetaTitle()
    {
        return $this->metaTitle;
    }

    public function setMetaTitle(?string $metaTitle = null): Section
    {
        $this->metaTitle = $metaTitle;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getMetaDescription()
    {
        return $this->metaDescription;
    }

    public function setMetaDescription(?string $metaDescription = null): Section
    {
        $this->metaDescription = $metaDescription;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getMetaKeywords()
    {
        return $this->metaKeywords;
    }

    public function setMetaKeywords(?string $metaKeywords = null): Section
    {
        $this->metaKeywords = $metaKeywords;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getFeedHeureka()
    {
        return $this->feedHeureka;
    }

    /**
     * @return int|null
     */
    public function getFeedHeurekaSk()
    {
        return $this->feedHeurekaSk;
    }

    public function setFeedHeurekaSk(?int $feedHeurekaSk = null): Section
    {
        $this->feedHeurekaSk = $feedHeurekaSk;

        return $this;
    }

    public function setFeedHeureka(?int $feedHeureka = null): Section
    {
        $this->feedHeureka = $feedHeureka;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getFeedGoogle()
    {
        return $this->feedGoogle;
    }

    public function setFeedGoogle(?int $feedGoogle = null): Section
    {
        $this->feedGoogle = $feedGoogle;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getFeedGlami()
    {
        return $this->feedGlami;
    }

    public function setFeedGlami(?int $feedGlami = null): Section
    {
        $this->feedGlami = $feedGlami;

        return $this;
    }

    public function getFlags(): array
    {
        return $this->flags;
    }

    public function setFlags(array $flags): Section
    {
        $this->flags = $flags;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getFeedSeznam()
    {
        return $this->feedSeznam;
    }

    public function setFeedSeznam(?int $feedSeznam = null): Section
    {
        $this->feedSeznam = $feedSeznam;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getIdSlider()
    {
        return $this->idSlider;
    }

    public function setIdSlider(?int $idSlider = null): Section
    {
        $this->idSlider = $idSlider;

        return $this;
    }

    /**
     * @return array|mixed
     */
    public function getData()
    {
        if (!$this->data) {
            return [];
        }

        return json_decode($this->data, true);
    }

    /**
     * @param $data array|null
     *
     * @return $this
     */
    public function setData($data)
    {
        $this->data = ($data === null) ? $data : json_encode($data);

        return $this;
    }

    /**
     * @return string
     */
    public function getVirtual()
    {
        return $this->virtual;
    }

    /**
     * @param $virtual string
     *
     * @return $this
     */
    public function setVirtual($virtual)
    {
        $this->virtual = $virtual;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getPriority()
    {
        return $this->priority;
    }

    /**
     * @param $priority int|null
     *
     * @return $this
     */
    public function setPriority($priority)
    {
        $this->priority = $priority;

        return $this;
    }

    public function getProducersFilter(): string
    {
        return $this->producersFilter;
    }

    public function setProducersFilter(string $producersFilter): Section
    {
        $this->producersFilter = $producersFilter;

        return $this;
    }

    public function getChildren(): \Doctrine\Common\Collections\Collection
    {
        return $this->children;
    }

    public function getVisibleChildren(): \Doctrine\Common\Collections\Collection
    {
        return $this->getChildren()->filter(function ($section) {
            return $section->isVisible();
        });
    }

    public function setChildren(\Doctrine\Common\Collections\Collection $children): Section
    {
        $this->children = $children;

        return $this;
    }

    /**
     * @return Section[] id => Section pairs
     */
    public function getParents()
    {
        if (isset($this->parents)) {
            return $this->parents;
        }

        $parents = [$this->getId() => $this];
        $parent = $this;
        while ($parent = $parent->getParent()) {
            $parents[$parent->getId()] = $parent;
        }

        return $this->parents = array_reverse($parents, true);
    }

    /**
     * @return array id => name pairs
     *
     * @deprecated Use getParents() instead
     */
    public function getParentIDs(): array
    {
        $parentIDs = [];
        foreach ($this->getParents() as $parent) {
            /* @var $parent Section */
            $parentIDs[(string) $parent->getId()] = $parent->getName();
        }

        return $parentIDs;
    }

    /**
     * @return Section|null
     */
    public function getParent()
    {
        return $this->parent;
    }

    public function setParent(?Section $parent = null): Section
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * @return int
     */
    public function getCount()
    {
        return $this->count;
    }

    /**
     * @return Section
     */
    public function setCount(int $count)
    {
        $this->count = $count;

        return $this;
    }

    /**
     * @return int
     */
    public function getInStockCount()
    {
        return $this->inStockCount;
    }

    /**
     * @return Section
     */
    public function setInStockCount(int $inStockCount)
    {
        $this->inStockCount = $inStockCount;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getAnnotation()
    {
        return $this->annotation;
    }

    public function setAnnotation(?string $annotation = null): Section
    {
        $this->annotation = $annotation;

        return $this;
    }

    public function getSliders(): array
    {
        return $this->sliders;
    }

    public function setSliders(array $sliders): Section
    {
        $this->sliders = $sliders;

        return $this;
    }

    public function getTemplate(): ?string
    {
        if ($this->template) {
            return "section/{$this->template}.tpl";
        }

        return $this->template;
    }

    public function setTemplate(string $template): Section
    {
        $this->template = $template;

        return $this;
    }

    public function isVisible(): bool
    {
        $dbcfg = \Settings::getDefault();

        if (($this->getFigure() == 'N') || ($this->getFigure() == 'O')) {
            return false;
        }

        return $dbcfg->cat_show_empty != 'N'
            || $this->getRedirectUrl()
            || ($dbcfg->prod_show_not_in_store != 'N' && $this->getCount() > 0)
            || $this->getInStockCount() > 0;
    }

    public function isVirtual()
    {
        return (bool) ($this->getVirtual() == 'Y');
    }

    protected function getLevel(): int
    {
        return count($this->getParents()) - 1;
    }

    /**
     * BC getter.
     *
     * @return string
     *
     * @deprecated
     */
    protected function getUid()
    {
        $uid = '';
        foreach ($this->getParents() as $parent) {
            /* @var $parent Section */
            $uid .= '_'.$parent->getId();
        }

        return trim($uid, '_');
    }

    /**
     * BC getter.
     *
     * @return array of VISIBLE sections only
     *
     * @deprecated
     */
    protected function getSubmenu()
    {
        $children = [];
        foreach ($this->getChildren() as $child) {
            if ($child->isVisible() || isAdministration()) { // TODO: Omlouvám se za admin HACK :-( Ale je to na spoustě míst. V adminu má být vidět všechno.
                $children[$child->getId()] = $child;
            }
        }

        return $children;
    }

    public function offsetExists($offset): bool
    {
        return method_exists($this, $this->getterMethod($offset));
    }

    protected function getterMethod($offset)
    {
        // backward compatible transformation
        // also take a look at methods getUid, getSubmenu
        $bcTransformation = static::bcTransformation();

        return 'get'.ucfirst(
            isset($bcTransformation[$offset]) ? $bcTransformation[$offset] : $offset
        );
    }

    public static function bcTransformation()
    {
        return [
            'id_block' => 'idBlock',
            'title' => 'name',
            'title_short' => 'nameShort',
            'list_variations' => 'listVariations',
            'lead_figure' => 'leadFigure',
            'lead_text' => 'leadText',
            'lead_products' => 'leadProducts',
            'meta_title' => 'metaTitle',
            'meta_description' => 'metaDescription',
            'feed_heureka' => 'feedHeureka',
            'feed_google' => 'feedGoogle',
            'id_slider' => 'idSlider',
            'feed_seznam' => 'feedSeznam',
            'producers_filter' => 'producersFilter',
            'parents' => 'parentIDs',
            'url' => 'Url',
            'link' => 'fullUrl',
            'date_updated' => 'dateUpdated',
        ];
    }

    public function &offsetGet($offset): mixed
    {
        $method = $this->getterMethod($offset);
        if (method_exists($this, $method)) {
            $res = call_user_func([$this, $method]);

            return $res;
        }
        $res = null;

        return $res;
    }

    /**
     * Offset to set.
     *
     * @see http://php.net/manual/en/arrayaccess.offsetset.php
     *
     * @param mixed $offset <p>
     *                      The offset to assign the value to.
     *                      </p>
     * @param mixed $value  <p>
     *                      The value to set.
     *                      </p>
     *
     * @since 5.0.0
     */
    public function offsetSet($offset, $value): void
    {
    }

    /**
     * Offset to unset.
     *
     * @see http://php.net/manual/en/arrayaccess.offsetunset.php
     *
     * @param mixed $offset <p>
     *                      The offset to unset.
     *                      </p>
     *
     * @since 5.0.0
     */
    public function offsetUnset($offset): void
    {
    }

    public function jsonSerialize(): mixed
    {
        $fields = [
            '_fields' => array_keys(static::bcTransformation()),
        ];

        $f = new \ReflectionClass($this);
        foreach ($f->getMethods() as $method) {
            if (!$method->isPublic() || !StringUtil::startsWith($method->name, 'get')) {
                continue;
            }

            $name = lcfirst(substr($method->name, 3));
            if (!in_array($name, array_values(static::bcTransformation()))) {
                $fields['_fields'][] = $name;
            }
        }

        return $fields;
    }

    public function displayProductsFromSubsections(): bool
    {
        return $this->getBehaviour() == self::DISPLAY_PRODUCTS_FROM_SUBSECTIONS;
    }

    public function getProducersToTitle(): ?string
    {
        return $this->producersToTitle;
    }

    public function setProducersToTitle(?string $producersToTitle): void
    {
        $this->producersToTitle = $producersToTitle;
    }

    public function getProducersIndexing(): ?string
    {
        return $this->producersIndexing;
    }

    public function setProducersIndexing(?string $producersIndexing): void
    {
        $this->producersIndexing = $producersIndexing;
    }

    public function getLabelsFilter(): ?string
    {
        return $this->labelsFilter;
    }

    public function setLabelsFilter(?string $labelsFilter): void
    {
        $this->labelsFilter = $labelsFilter;
    }

    /**
     * @param Section[]|null $parents
     */
    public function setParents(?array $parents): void
    {
        $this->parents = $parents;
    }
}
