<?php

namespace Comitium5\DesignerBundle\Publisher\Page;

use Comitium5\DesignerBundle\Manager\Designer\DesignerManager;
use Comitium5\DesignerBundle\Manager\Page\PageManager;
use Comitium5\DesignerBundle\Model\Interfaces\LocaleInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\DesignerPageInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PagePublisherInterface;
use Comitium5\DesignerBundle\Model\Interfaces\PublisherInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Subsite\SubsiteInterface;
use Comitium5\DesignerBundle\Provider\SubsiteProvider;
use Comitium5\DesignerBundle\Publisher\FrontPublisher;
use Comitium5\DesignerBundle\Publisher\Publisher;
use Comitium5\DesignerBundle\Publisher\Worker\PageAfterPublication;

/**
 * Class PagePublisher
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Publisher\Page
 */
class PagePublisher implements PagePublisherInterface
{
    const TARGET_FOLDER = "Resources/views/cache";

    /**
     * @var DesignerManager
     */
    protected $designerManager;

    /**
     * @var PageContentResolver
     */
    protected $resolver;

    /**
     * @var FrontPublisher
     */
    protected $publisher;

    /**
     * @var PublisherInterface
     */
    protected $tmpPublisher;

    /**
     * @var string
     */
    protected $tmpPath;

    /**
     * @var PageManager
     */
    protected $pageManager;

    /**
     * @var PageAfterPublication
     */
    protected $worker;

    /**
     * @var SubsiteProvider
     */
    protected $subsiteProvider;

    /**
     * PagePublisher constructor.
     *
     * @param PageContentResolver $pageResolver
     * @param DesignerManager $designerManager
     * @param FrontPublisher $publisher
     * @param Publisher $tmpPublisher
     * @param PageManager $pageManager
     * @param PageAfterPublication $worker
     * @param SubsiteProvider $subsiteProvider
     */
    public function __construct(
        PageContentResolver $pageResolver,
        DesignerManager $designerManager,
        FrontPublisher $publisher,
        Publisher $tmpPublisher,
        PageManager $pageManager,
        PageAfterPublication $worker,
        SubsiteProvider $subsiteProvider
    ) {
        $this->resolver = $pageResolver;
        $this->designerManager = $designerManager;
        $this->publisher = $publisher;
        $this->tmpPublisher = $tmpPublisher;
        $this->pageManager = $pageManager;
        $this->worker = $worker;
        $this->subsiteProvider = $subsiteProvider;
    }

    /**
     * @param DesignerPageInterface $page
     * @param LocaleInterface|null $locale
     *
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function publish(
        DesignerPageInterface $page,
        LocaleInterface $locale = null
    ): void {
        $success = $this
            ->designerManager
            ->generateFilePage($page);

        if (!$success) {
            return;
        }

        $publishContent = $this->generatePublishContentData($page, $locale);

        $this
            ->publisher
            ->publish($publishContent, $page);

        $this
            ->pageManager
            ->publish($page, $locale);

        $this->workAfterPublish($page);
    }

    /**
     * @param DesignerPageInterface $page
     * @param LocaleInterface|null $locale
     *
     * @throws \Exception
     */
    public function unPublish(
        DesignerPageInterface $page,
        LocaleInterface $locale = null
    ): void {
        $publishContent = $this->generateUnPublishContentData($page, $locale);

        $this
            ->publisher
            ->removeFiles($publishContent);

        $this
            ->pageManager
            ->unPublish($page);

        $this->workAfterPublish($page);
    }

    /**
     * @param PageInterface $page
     * @param SubsiteInterface $subSite
     * @param LocaleInterface|null $locale
     *
     * @throws \Exception
     */
    public function publishTmp(
        PageInterface $page,
        SubsiteInterface $subSite,
        LocaleInterface $locale = null
    ) {
        $this->resolver->setIsTmpMode(true);
        $publishContent = [];

        foreach ($subSite->getLocaleDomains() as $localeDomain) {
            $content  = $this
                ->generatePageContent($page, $localeDomain, $locale);

            if (empty($content)) {
                continue;
            }

            $filename = $this
                ->resolver
                ->resolveCacheFileName($page);

            $publishContent[$filename] = $content["content"];

            foreach ($content["statics"] as $staticPath => $staticContent) {
                $publishContent[$staticPath] = $staticContent;
            }
        }

        $this
            ->tmpPublisher
            ->publishToDB($publishContent, $page);
    }

    /**
     * @param PageInterface $page
     * @param LocaleInterface|null $locale
     *
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException|\Exception
     */
    protected function generatePublishContentData(
        PageInterface $page,
        LocaleInterface $locale = null
    ) {
        $publishContent = [];

        foreach ($this->subsiteProvider->getSubsiteLocales() as $localeDomain) {
            $content  = $this->generatePageContent($page, $localeDomain, $locale);

            if (empty($content)) {
                continue;
            }

            $filename = $this
                ->resolver
                ->resolveCacheFileName($page);

            $publishContent[$this->buildFrontPath($filename)] = $content["content"];

            foreach ($content["statics"] as $staticPath => $staticContent) {
                $publishContent[$this->buildFrontPath($staticPath)] = $staticContent;
            }
        }

        return $publishContent;
    }

    /**
     * @return FrontPublisher
     */
    public function getPublisher()
    {
        return $this->publisher;
    }

    /**
     * @param PublisherInterface $publisher
     */
    public function setPublisher(PublisherInterface $publisher)
    {
        $this->publisher = $publisher;
    }

    /**
     * @param $relative
     *
     * @return string
     */
    protected function buildFrontPath($relative)
    {
        return DIRECTORY_SEPARATOR.self::TARGET_FOLDER.DIRECTORY_SEPARATOR.$relative;
    }

    /**
     * @param PageInterface $page
     * @param LocaleInterface $localeDomain
     * @param LocaleInterface|null $locale
     *
     * @return array|mixed|null
     * @throws \Exception
     */
    protected function generatePageContent(
        PageInterface $page,
        LocaleInterface $localeDomain,
        LocaleInterface $locale = null
    ) {
        if ($locale !== null) {
            if ($localeDomain->getCode() !== $locale->getCode()) {
                return null;
            }

            $this->resolver->setLocale($locale);
        } else {
            $this->resolver->setLocale($localeDomain);
        }

        return $this
            ->resolver
            ->resolve($page);
    }

    /**
     * @return PageContentResolver
     */
    public function getResolver()
    {
        return $this->resolver;
    }

    /**
     * @param PageContentResolver $resolver
     * @return $this
     */
    public function setResolver(PageContentResolver $resolver)
    {
        $this->resolver = $resolver;

        return $this;
    }

    /**
     * @param PageInterface $page
     * @param LocaleInterface|null $locale
     *
     * @return array
     * @throws \Exception
     */
    protected function generateUnPublishContentData(
        PageInterface $page,
        LocaleInterface $locale = null
    ) {
        $publishContent = [];

        foreach ($this->subsiteProvider->getSubsiteLocales() as $localeDomain) {
            if ($locale !== null) {
                if ($localeDomain !== $locale) {
                    continue;
                }

                $resolveLocale = $locale;
            } else {
                $resolveLocale = $localeDomain;
            }

            $this
                ->resolver
                ->setLocale($resolveLocale);

            $filename = $this
                ->resolver
                ->resolveCacheFileName($page);

            $publishContent[] = $this->buildFrontPath($filename);
        }
        return $publishContent;
    }

    /**
     * @param $page
     *
     * @throws \Exception
     */
    protected function workAfterPublish($page)
    {
        $worker = $this->worker;

        if ($page instanceof PageInterface) {
            $worker->addPage($page);
        } else {
            $worker->setPages($page);
        }

        $worker->work();
    }
}
