<?php

// ##############################################################
//	TENTO SKRIPT NAPROGRAMOVALO STUDIO wpj s.r.o.
//	UZIVANI TOHOTO DILA JE MOZNE POUZE SE SOUHLASEM AUTORA
//	KONTAKT: joe@wpj.cz
// ##############################################################

use KupShop\CatalogBundle\Util\SectionUtil;
use KupShop\ContentBundle\Util\ImageLocator;
use KupShop\ContentBundle\Util\SliderUtil;
use KupShop\I18nBundle\Translations\SlidersImagesTranslation;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Query\Operator;
use Query\QueryBuilder;

class Sliders extends Window
{
    public const ON_PAGE = 20;

    protected SectionUtil $sectionUtil;
    protected SliderUtil $sliderUtil;

    public function __construct()
    {
        $this->sectionUtil = ServiceContainer::getService(SectionUtil::class);
        $this->sliderUtil = ServiceContainer::getService(SliderUtil::class);
    }

    public function get_vars()
    {
        $ID = $this->getID();
        $vars = parent::get_vars();
        $pageVars = getVal('body', $vars);
        $pageVars['ID'] = $ID;

        $pageVars['data']['default_size'] = getVal('size', $GLOBALS['cfg']['Modules']['sliders'], [780, 400]);
        if ($ID == 1) {
            $pageVars['data']['default_size'] = getVal('size_index', $GLOBALS['cfg']['Modules']['sliders'], $pageVars['data']['size']);
        }

        $pager = $this->createPager();

        $SQL = sqlQueryBuilder()
            ->select('SQL_CALC_FOUND_ROWS si.*')
            ->from('sliders_images', 'si')
            ->where(Operator::equals(['si.id_slider' => $ID]))
            ->andWhere($pager->getSpec())
            ->orderBy('position', 'ASC')
            ->execute()->fetchAll();

        $total_count = (int) sqlFetchAssoc(sqlQuery('SELECT FOUND_ROWS() as total_count'))['total_count'];
        $pager->setTotal($total_count);

        $photos = sqlFetchAll(sqlQueryBuilder()
            ->select('*')
            ->from('photos')
            ->where(Operator::inIntArray(array_map(function ($x) { return $x['id_photo']; }, $SQL), 'id'))
            ->execute(), 'id');

        $imageLocator = ServiceContainer::getService(ImageLocator::class);

        $images = [];
        foreach ($SQL as $row) {
            $row['img'] = $row['id_photo'] ? $imageLocator->getImage($photos[$row['id_photo']]) : null;
            $row['active'] = $this->isSliderActive($row);
            $this->unserializeCustomData($row);
            $row['sliderTranslationsFigure'] = $this->getTranslationUtil()?->getTranslationsFigure(SlidersImagesTranslation::class, $row['id']);
            $images[] = $row;
        }
        $pageVars['images'] = $images;

        if (findModule(\Modules::PRODUCTS_SECTIONS)) {
            $pageVars['data']['slider_sections'] = $this->fetchSliderSections((int) $ID);
        }

        $this->unserializeCustomData($pageVars['data']);

        $vars['body'] = $pageVars;
        $vars['pager'] = $pager;

        return $vars;
    }

    public function createPager()
    {
        $pager = new \Pager();
        $pager->setOnPage(self::ON_PAGE);
        $pager->setPageNumber((int) getVal('page', null, 1));
        $pager->setUrl($_SERVER['REQUEST_URI']);

        return $pager;
    }

    public function getData()
    {
        $data = parent::getData();
        if (getVal('Submit')) {
            $this->serializeCustomData($data);
        }

        return $data;
    }

    public function handleUpdate()
    {
        $OLD_ID = $this->getID();
        $SQL = parent::handleUpdate();

        $ID = $this->getID();

        if ($duplicate = $this->isDuplicate()) {
            // duplicate superuser fields (sizes)
            sqlQueryBuilder()
                ->update('sliders', 's')
                ->join('s', 'sliders', 'so', 'so.id = :oldId')
                ->set('s.size', 'so.size')
                ->set('s.size_tablet', 'so.size_tablet')
                ->set('s.size_mobile', 'so.size_mobile')
                ->where(Operator::equals(['s.id' => $this->getID()]))
                ->setParameter('oldId', $OLD_ID)
                ->execute();

            // duplicate sliders
            $duplicate_sliders = sqlFetchAll($this->selectSQL('sliders_images', ['id_slider' => $OLD_ID]), 'id');
            foreach ($duplicate_sliders as &$img) {
                $img = $this->insertSlidersImage($img);
            }
        }

        $data = $this->getData();
        $sliders = getVal('images', $data, []);
        krsort($sliders);
        foreach ($sliders as $id => &$img) {
            $photoId = empty($img['id_photo']) ? null : $img['id_photo'];
            if (!empty($img['delete']) || !$id) {
                if ($id > 0) {
                    $delete_id = $img['id'];
                    if ($duplicate) {
                        $delete_id = $duplicate_sliders[$img['id']]['id'] ?? $img['id'];
                    }
                    $this->deleteSQL('sliders_images', ['id' => $delete_id, 'id_slider' => $ID]);
                }
                continue;
            }

            $img['date_from'] = ($img['date_from'] == '') ? null : $this->prepareDateTime($img['date_from']);
            $img['date_to'] = ($img['date_to'] == '') ? null : $this->prepareDateTime($img['date_to']);
            $this->serializeCustomData($img);

            $uploadedImages = $this->getUploadedImages((int) $id);
            $translateSlidersFigure = $img['translation_figure'] ?? [];
            unset($img['translation_figure']);

            if ($id < 0) {
                // Skip if desktop image is missing
                if (empty($uploadedImages['desktop'])) {
                    continue;
                }

                // upload new photos when adding slider
                if ($photoId = $this->uploadImages($uploadedImages, null)) {
                    $img['id_photo'] = $photoId;
                    $img['position'] = 0;
                    $img = $this->insertSlidersImage($img);
                }
            } else {
                if ($duplicate) {
                    if ($duplicate_img = ($duplicate_sliders[$img['id']] ?? null)) {
                        unset($img['id']);
                        $duplicate_img = array_replace($duplicate_img, $img);
                        $this->updateSQL('sliders_images', $duplicate_img, ['id' => $duplicate_img['id']]);
                        $img = $duplicate_img;
                    }
                } else {
                    // upload photos to existing slider (upload of additional photo versions)
                    if ($photoId) {
                        $this->uploadImages($uploadedImages, $photoId);
                    }

                    $this->updateSQL('sliders_images', $img, ['id' => $img['id']]);
                }

                $this->getTranslationUtil()?->updateTranslationsFigure(SlidersImagesTranslation::class, $img['id'], $translateSlidersFigure);
            }
        }

        if (findModule(\Modules::PRODUCTS_SECTIONS)) {
            $this->saveSliderSections(getVal('slider_sections', $data, []));
        }

        sqlQuery('SELECT @i := 0; UPDATE sliders_images SET position = (select @i := @i + 1) WHERE id_slider=:id ORDER BY position', ['id' => $ID]);

        MenuSectionTree::invalidateCache();
        clearCache('sliders', true);

        if ($page = getVal('page')) {
            $_GET['page'] = $page;
        }

        $this->activityMessage($data['name']);
        $this->returnOK($GLOBALS['txt_str']['status']['saved']);
    }

    public function uploadImages(array $images, ?int $photoId): int
    {
        $versions = [
            'desktop' => Photos::PHOTO_VERSION_DESKTOP,
            'tablet' => Photos::PHOTO_VERSION_TABLET,
            'mobile' => Photos::PHOTO_VERSION_MOBILE,
        ];

        foreach ($images as $version => $image) {
            if (!empty($image)) {
                $img = Photos::get($photoId, $versions[$version]);
                $img->uploadPhotoOrVideo($image, $image['name']);

                if ($photoId === null) {
                    $photoId = (int) $img->getID();
                }
            }
        }

        return $photoId;
    }

    public function getUploadedImages(int $id): array
    {
        $images = $_FILES['image'] ?? [];

        $result = [];
        foreach (['desktop', 'tablet', 'mobile'] as $type) {
            $result[$type] = ($images['size'][$id][$type] ?? 0) <= 0 ? [] : [
                'name' => $images['name'][$id][$type],
                'type' => $images['type'][$id][$type],
                'tmp_name' => $images['tmp_name'][$id][$type],
                'error' => $images['error'][$id][$type],
                'size' => $images['size'][$id][$type],
            ];
        }

        return $result;
    }

    protected function insertSlidersImage($img)
    {
        unset($img['id']);
        $img['id_slider'] = $this->getID();
        $this->insertSQL('sliders_images', $img);
        $img['id'] = sqlInsertId();

        return $img;
    }

    protected function isSliderActive(array $slider): bool
    {
        $today = new DateTime();
        $dateFrom = $slider['date_from'] ? new DateTime($slider['date_from']) : null;
        $dateTo = $slider['date_to'] ? new DateTime($slider['date_to']) : null;

        if (($dateFrom === null || $dateFrom < $today) && ($dateTo === null || $dateTo > $today)) {
            return true;
        }

        return false;
    }

    public function handleDrag()
    {
        if (($moved_item = getVal('moved_item')) && ($ID = $this->getID())) {
            $this->updateSQL('sliders_images', ['position' => 0], ['id' => $moved_item, 'id_slider' => $ID]);
            sqlQuery('SELECT @i := 0; UPDATE sliders_images SET position = (select @i := @i + 1) WHERE id_slider=:id ORDER BY position', ['id' => $ID]);
        }
        $this->returnOK();
    }

    public function saveSliderSections(array $sliderSections): void
    {
        $sliderSections = array_map(fn (string $json) => json_decode_strict($json, true), $sliderSections);

        sqlGetConnection()->transactional(function () use ($sliderSections) {
            $this->sliderUtil->clearSliderSections(idSlider: (int) $this->getID());
            $this->sliderUtil->saveSliderPositions($sliderSections, ['id_slider']);
        });
    }

    protected function fetchSliderSections(int $sliderId): array
    {
        $sectionIds = sqlQueryBuilder()->select('sis.id_section AS id')
            ->from('sliders_in_sections', 'sis')
            ->andWhere(Operator::equals(['sis.id_slider' => $sliderId]));

        $qb = $this->createSliderSectionsQB()
            ->andWhere(Operator::inSubquery('s.id', $sectionIds))
            ->groupBy('s.id_rootsection', 'sis.id_section', 'sis.id_slider', 'sis.position');

        $sliderSections = [];
        foreach ($qb->execute() as $flatSection) {
            $sectionId = $flatSection['id_section'];

            if (!isset($sliderSections[$sectionId])) {
                $sliderSections[$sectionId] = [
                    'id' => $sectionId,
                    'full_path' => $flatSection['full_path'],
                    'name' => $flatSection['section_name'],
                    'badges' => $this->getBadges($flatSection),
                    'positions' => $this->getEmptyPositions(),

                    'id_rootsection' => $flatSection['id_rootsection'],
                    'full_position' => $flatSection['full_position'],
                    'section_position' => $flatSection['section_position'],
                ];
            }

            $sliderSections[$sectionId]['positions'][$flatSection['position']] = $flatSection;
        }

        $orderedSections = [];
        foreach ($sliderSections as $section) {
            $orderedSections[$section['id_rootsection']][] = $section;
        }

        foreach ($orderedSections as $orderedSection) {
            uasort($orderedSection, function ($a, $b) {
                return $a['section_position'] <=> $b['section_position'];
            });
        }

        return array_reduce($orderedSections, fn ($carry, $sections) => array_merge($carry, $sections), []);
    }

    protected function createSliderSectionsQB(?int $idTopsection = null): QueryBuilder
    {
        $whereSection = isset($idTopsection) ? 'id_topsection = :idTopSection' : 'id_topsection IS NULL';

        $recursiveSections = "
            WITH RECURSIVE cte (id, id_rootsection, id_topsection, name, figure, virtual, show_in_search, full_path, position, full_position) as (
                SELECT     id_section,
                           id_section AS id_rootsection,
                           id_topsection,
                           (SELECT name FROM sections WHERE id = id_section LIMIT 1) as name,
                           (SELECT figure FROM sections WHERE id = id_section LIMIT 1) as figure,
                           (SELECT virtual FROM sections WHERE id = id_section LIMIT 1) as virtual,
                           (SELECT show_in_search FROM sections WHERE id = id_section LIMIT 1) as show_in_search,
                           (SELECT name FROM sections WHERE id = id_section LIMIT 1) as full_path,
                           position,
                           CAST(LPAD(position, 5, 0) AS CHAR(500)) AS full_position
                FROM       sections_relation
                WHERE      {$whereSection}
                UNION ALL
                SELECT     sr.id_section,
                           cte.id_rootsection,
                           sr.id_topsection,
                           (SELECT name FROM sections WHERE id = sr.id_section LIMIT 1) as name,
                           (SELECT figure FROM sections WHERE id = sr.id_section LIMIT 1) as figure,
                           (SELECT virtual FROM sections WHERE id = sr.id_section LIMIT 1) as virtual,
                           (SELECT show_in_search FROM sections WHERE id = sr.id_section LIMIT 1) as show_in_search,
                           CONCAT(cte.full_path,' > ',(SELECT name FROM sections WHERE id = sr.id_section LIMIT 1)) as full_path,
                           CONCAT(full_position, '/', LPAD(sr.position, 5, 0)),
                           sr.position
                FROM       sections_relation sr
                INNER JOIN cte
                        on sr.id_topsection = cte.id
            ) SELECT * FROM cte";

        return sqlQueryBuilder()->select(
            's.id AS id_section',
            's.name AS section_name',
            's.figure',
            's.virtual',
            's.show_in_search',
            's.full_path',
            'sis.id_slider',
            'sis.position',
            'sl.name AS slider_name',
            's.id_topsection',
            's.position AS section_position',
            's.full_position',
            's.id_rootsection'
        )
            ->from("({$recursiveSections})", 's')
            ->leftJoin('s', 'sliders_in_sections', 'sis', 'sis.id_section = s.id')
            ->leftJoin('sis', 'sliders', 'sl', 'sis.id_slider = sl.id');
    }

    protected function getBadges(array $section): array
    {
        if (!$section) {
            return [];
        }

        $badges = $this->sectionUtil->getBadges(
            figure: $section['figure'],
            virtual: $section['virtual'],
            showInSearch: $section['show_in_search'],
        );

        foreach ($badges as &$badge) {
            $badge['title'] = translate($badge['translate_key'], 'sections');
        }

        return $badges;
    }

    protected function getEmptyPositions(): array
    {
        return array_fill_keys(array_keys($this->sliderUtil::getPositions()), null);
    }

    public function handleAjaxSection(): never
    {
        $idSection = getVal('id_section');
        if (!is_numeric($idSection)) {
            throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException('id_section is required');
        }

        $sections = $this->createSliderSectionsQB()
            ->andWhere(Operator::equals(['s.id' => $idSection]));

        $result = [];
        foreach ($sections->execute() as $sectionRow) {
            if (empty($result)) {
                $result = [
                    'id' => $idSection,
                    'name' => $sectionRow['section_name'],
                    'full_path' => $sectionRow['full_path'],
                    'badges' => $this->getBadges($sectionRow),
                    'positions' => $this->getEmptyPositions(),
                ];
            }

            if (!empty($sectionRow['id_slider'])) {
                $result['positions'][$sectionRow['position']] = [
                    'id_slider' => $sectionRow['id_slider'],
                    'slider_name' => $sectionRow['slider_name'],
                ];
            }
        }

        header('Content-Type: application/json');
        echo json_encode($result);

        exit;
    }
}

$sliders = new Sliders();
$sliders->run();
