<?php

namespace KupShop\ContentBundle\Controller;

use KupShop\AdminBundle\Util\BlocksHistory;
use KupShop\CDNBundle\CDN;
use KupShop\ContentBundle\Util\BlocekSettings;
use KupShop\ContentBundle\Util\Block;
use KupShop\ContentBundle\View\BlockEditView;
use KupShop\ContentBundle\View\BlockHistoryView;
use KupShop\I18nBundle\Translations\PhotosTranslation;
use KupShop\KupShopBundle\Query\JsonOperator;
use KupShop\KupShopBundle\Util\Functional\Mapping;
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
use KupShop\KupShopBundle\Util\StringUtil;
use KupShop\KupShopBundle\Util\System\PathFinder;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

class BlockController extends AbstractController
{
    use \DatabaseCommunication;

    public function __construct(protected ?CDN $cdn = null)
    {
    }

    /**
     * @Route("/_blocek/upload/")
     */
    public function uploadAction(Request $request)
    {
        try {
            $this->doCheckPermissions();
        } catch (\Exception $e) {
            return new JsonResponse([
                'result' => false,
                'error' => $e->getMessage(),
            ]);
        }

        $result = false;
        $message = null;
        try {
            $result = sqlGetConnection()->transactional(
                function () use ($request) {
                    $uploadedIds = [];

                    $photos = new \Photos('none');

                    /** @var UploadedFile $file */
                    foreach ($request->files->get('file', []) as $file) {
                        $data = [
                            'name' => $file->getClientOriginalName(),
                            'tmp_name' => $file->getRealPath(),
                        ];

                        $photos->newImage();
                        $photos->uploadImage($data);
                        $photoId = $photos->insertImageIntoDB();
                        if (!$photoId) {
                            throw new \Exception('Nahrání se nepodařilo, zkuste to znovu.');
                        }

                        $uploadedIds[] = $photoId;
                    }

                    return $uploadedIds;
                }
            );
        } catch (\Exception $e) {
            $message = $e->getMessage();
        }

        return new JsonResponse([
            'result' => $result,
            'message' => $message,
        ]);
    }

    #[Route('/_blocek/video-info/')]
    public function videoInfoAction(Request $request, PathFinder $pathFinder): JsonResponse
    {
        $result = false;
        $message = null;
        $data = [];
        try {
            if (!findModule(\Modules::CDN) || !$this->cdn) {
                throw new \Exception('Není povolen modul CDN!');
            }

            $videos = $this->getPhotosData((array) $request->get('videoId', []), function (QueryBuilder $qb) {
                $qb->leftJoin('ph', 'videos', 'v', 'ph.id = v.id_photo');
                $qb->addSelect('v.*');
            });

            foreach ($videos as $video) {
                if (!$video['id_cdn']) {
                    throw new \Exception('Video není nahráno v CDN!');
                }

                $data[] = [
                    'id' => (int) $video['id'],
                    'title' => $video['descr'],
                    'id_cdn' => $video['id_cdn'],
                    'url_mp4' => $this->cdn->getMP4Video($video['id_cdn'], '720p'),
                    'url_hls' => $this->cdn->getHLSPlaylist($video['id_cdn']),
                    'thumbnail' => $this->cdn->getThumbnail($video['id_cdn'])['url'],
                ];
            }

            $result = true;
        } catch (\Exception $e) {
            $message = $e->getMessage();
            $data = [];
        }

        return $this->jsonResponse(result: $result, data: $data, message: $message);
    }

    /**
     * @Route("/_blocek/photo-info/")
     */
    public function photoInfoAction(Request $request, PathFinder $pathFinder): JsonResponse
    {
        $result = false;
        $message = null;
        $data = [];
        try {
            foreach ($this->getPhotosData((array) $request->get('photoId', [])) as $photo) {
                $file = $pathFinder->getDataDir().'photos/'.$photo['source'].$photo['image_2'];
                if (!file_exists($file)) {
                    throw new \Exception('Obrázek nebyl nalezen!');
                }

                [$width, $height] = getimagesize($file);

                $data[] = [
                    'id' => (int) $photo['id'],
                    'width' => $width,
                    'height' => $height,
                    'timestamp' => filemtime($file),
                    'title' => $photo['descr'],
                ];
            }

            $result = true;
        } catch (\Exception $e) {
            $message = $e->getMessage();
            $data = [];
        }

        return $this->jsonResponse(result: $result, data: $data, message: $message);
    }

    private function jsonResponse(bool $result, array $data, ?string $message = null): JsonResponse
    {
        return new JsonResponse(
            [
                'result' => $result,
                'message' => $message,
                'data' => $data,
            ],
            200,
            [
                'Access-Control-Allow-Origin' => '*',
            ]
        );
    }

    private function getPhotosData(array $ids, ?callable $specs = null): array
    {
        $photos = sqlQueryBuilder()
            ->select('ph.*')
            ->from('photos', 'ph')
            ->andWhere(Operator::inIntArray($ids, 'ph.id'))
            ->andWhere(Translation::coalesceTranslatedFields(PhotosTranslation::class));

        if ($specs) {
            $photos->andWhere($specs);
        }

        return $photos->execute()->fetchAllAssociative();
    }

    /**
     * @Route("/_blocek/edit-block/{id}/")
     */
    public function editBlockAction(Request $request, Block $block, BlockEditView $blockEditView, $id)
    {
        $blockEditView->setBlockId($id);

        return $blockEditView->getResponse();
    }

    /**
     * @Route("/_blocek/history-block/{historyId}/{blockId}/")
     */
    public function historyBlockAction(Request $request, Block $block, BlockHistoryView $blockHistoryView, $historyId, $blockId)
    {
        $blockHistoryView->setBlockId($blockId);
        $blockHistoryView->setHistoryId($historyId);

        return $blockHistoryView->getResponse();
    }

    /**
     * @Route("/_blocek/save-block/")
     */
    public function saveBlockAction(Request $request, SentryLogger $sentryLogger, BlocksHistory $blocksHistory, Block $blockUtil)
    {
        try {
            $this->doCheckPermissions();
        } catch (\Exception $e) {
            return new JsonResponse([
                'result' => false,
                'error' => $e->getMessage(),
            ]);
        }

        $data_array = json_decode($request->getContent(), true);

        try {
            $result = sqlGetConnection()->transactional(
                function () use ($blockUtil, $data_array, $blocksHistory) {
                    foreach ($data_array as $data) {
                        if ($data['id_block'] = $this->getBlockId($blockUtil,
                            $data['id_block'] ?: null,
                            $data['object_type'] ?: null,
                            $data['object_id'] ?: null)) {
                            // get images blocks
                            $imagesBlocks = $this->getImageBlocks(json_decode($data['json_content'], true));

                            $language = ($data['id_language'] ?? null);
                            $data['json_content'] = json_encode(json_decode($data['json_content'], true));
                            $data['content'] = StringUtil::unicodeToEntities($data['content']);
                            if (!$language) {
                                $block = ['id' => $data['id_block'], 'name' => '', 'content' => $data['content']];
                                $blocksHistory->saveBlocksHistory([$data['id_block'] => $block]);
                                // save content
                                sqlQueryBuilder()->update('blocks')
                                    ->directValues(
                                        [
                                            'json_content' => $data['json_content'],
                                            'content' => $data['content'],
                                        ]
                                    )
                                    ->where(Operator::equals(['id' => $data['id_block']]))
                                    ->execute();

                                $rootId = sqlQueryBuilder()->select('id_root')->from('blocks')
                                    ->where(Operator::equals(['id' => $data['id_block']]))
                                    ->execute()->fetchColumn();
                                $pageID = sqlQueryBuilder()->select('id')->from('pages')
                                    ->where(Operator::equals(['id_block' => $rootId]))
                                    ->execute()->fetchColumn();
                                if ($pageID) {
                                    $this->updateSQL('pages', ['updated' => date('Y-m-d H:i:s')], ['id' => $pageID]);
                                }
                            } else {
                                // save content - translations
                                $originalName = sqlQueryBuilder()->select('name')->from('blocks_translations')
                                    ->where(Operator::equals([
                                        'id_block' => $data['id_block'],
                                        'id_language' => $data['id_language'],
                                    ]))->setMaxResults(1)->execute()->fetchColumn(0);
                                if ($originalName === false) {
                                    $this->insertSQL('blocks_translations',
                                        [
                                            'id_block' => $data['id_block'],
                                            'id_language' => $data['id_language'],
                                            'json_content' => $data['json_content'],
                                            'content' => $data['content'],
                                        ]);
                                } else {
                                    $this->updateSQL('blocks_translations',
                                        [
                                            'name' => $originalName,
                                            'json_content' => $data['json_content'],
                                            'content' => $data['content'],
                                            'updated' => date('Y-m-d H:i:s'),
                                        ],
                                        [
                                            'id_block' => $data['id_block'],
                                            'id_language' => $data['id_language'],
                                        ]);
                                }
                            }

                            // create photos relations
                            sqlQueryBuilder()->delete('photos_blocks_new_relation')
                                ->where(Operator::equals(['id_block' => $data['id_block']]))
                                ->execute();

                            if (!empty($imagesBlocks)) {
                                foreach ($imagesBlocks as $imageBlock) {
                                    if (isset($imageBlock['settings']['photo'])) {
                                        $imageBlock['settings']['photos'][0]['photo'] = $imageBlock['settings']['photo'];
                                    }
                                    $photos = $imageBlock['settings']['photos'];
                                    foreach ($photos as $ph) {
                                        try {
                                            $photoId = $ph['photo']['id'];
                                            sqlQueryBuilder()
                                                ->insert('photos_blocks_new_relation')
                                                ->directValues(
                                                    [
                                                        'id_photo' => $photoId,
                                                        'id_block' => $data['id_block'],
                                                    ]
                                                )->execute();
                                        } catch (\Exception $e) {
                                        }
                                    }
                                }
                            }
                        } else {
                            throw new \Exception('Nor id_block, object_type or object_id specified!');
                        }
                    }

                    return true;
                }
            );
        } catch (\Exception $e) {
            $sentryLogger->captureException($e);

            return new JsonResponse([
                'result' => false,
                'error' => $e->getMessage(),
            ]);
        }

        return new JsonResponse([
            'result' => $result,
        ]);
    }

    protected function getBlockId(Block $blockUtil, ?int $id_block, ?string $objectType, ?int $objectId)
    {
        if ($id_block ?? false) {
            return $id_block;
        }
        if (!$objectType || !$objectId) {
            return false;
        }

        return $blockUtil->insertFirstBlock($objectType, $objectId, '');
    }

    public function getImageBlocks($settings)
    {
        $result = [];
        foreach ($settings as $setting) {
            $imageBlocks = array_filter(
                $settings,
                function ($x) {
                    return isset($x['settings']['photo']) || isset($x['settings']['photos']);
                }
            );
            $result = array_merge($result, $imageBlocks);
            if (!empty($setting['children'])) {
                $result = array_merge($result, $this->getImageBlocks($setting['children']));
            }
        }

        return $result;
    }

    public function doCheckPermissions()
    {
        if (!getAdminUser()) {
            throw new NotFoundHttpException('Platnost přihlášení vypršela');
        }
    }

    /**
     * @Route("/_blocek/ProductsBlock/")
     */
    public function getProductsAction(Request $request, Block $blockUtil)
    {
        if (!$request->getContent()) {
            throw new NotFoundHttpException('Empty data');
        }

        $blocekData = json_decode_strict($request->getContent(), true);

        $filterSpecs = $blockUtil->getProductsBlockSpecs($blocekData);

        $smarty = createSmarty(false, true);
        $smarty->assign('spec', $filterSpecs);
        $smarty->assign('data', $blocekData);

        $response['html'] = $smarty->fetch('block.products.blocek.tpl');
        $response['result'] = true;
        $response['filter'] = $blocekData;

        // Detect empty product list in edit mode
        if ($request->get('editor') && strpos($response['html'], 'href') === false) {
            $response['result'] = false;
        }

        return new JsonResponse($response);
    }

    protected function getOrderDir($order_dir)
    {
        $order_dir = trim($order_dir);
        if (($order_dir != 'ASC') && ($order_dir != 'DESC')) {
            $order_dir = 'ASC';
        }

        return $order_dir;
    }

    protected function getOrderFields($order_by)
    {
        switch ($order_by) {
            case 'code':
                $order = 'p.code';
                break;
            case 'title':
                $order = 'p.title';
                break;
            case 'price':
                $order = 'p.price';
                break;
            case 'date':
                $order = 'p.date_added';
                break;
            case 'sell':
                $order = 'p.pieces_sold';
                break;
            case 'discount':
                $order = 'p.discount';
                break;
            case 'store':
                $order = 'p.in_store';
                break;
            case 'random':
                $order = 'RAND()';
                break;
            default:
                $order = 'p.title';
                break;
        }

        return $order;
    }

    #[Route('/_blocek/settings/')]
    public function getBlocekSettings(BlocekSettings $blocekSettings): JsonResponse
    {
        $blocekSettings->fetchSettings();

        return new JsonResponse(['message' => 'BlocekSettings', 'data' => $blocekSettings->asArray()]);
    }

    /**
     * @Route("/_blocek/templates/")
     */
    public function getTemplatesAction(Block $blockUtils)
    {
        $message = null;
        $data = [];

        if (!findModule(\Modules::TEMPLATES)) {
            return new JsonResponse(
                [
                    'message' => 'No templates module',
                    'data' => $data,
                ]
            );
        }

        $qb = sqlQueryBuilder()
            ->select('tc.*')
            ->from('templates_categories', 'tc')
            ->Where(Operator::like([JsonOperator::value('tc.data', 'show_in_editor') => 'Y']));

        $templateCategories = sqlFetchAll($qb->execute(), 'id');
        $templateCategoriesIDs = array_keys($templateCategories);

        $templates = sqlQueryBuilder()
            ->select('t.*')
            ->from('templates', 't')
            ->where(Operator::inIntArray($templateCategoriesIDs, 't.id_category'));

        foreach ($templates->execute() as $template) {
            $blocks = $blockUtils->getBlocks($template['id_block']);

            // beru obsah jen prvního bloku
            if ($blocks[0]['content'] ?? false) {
                $data[] = [
                    'name' => $template['name'],
                    'group' => $templateCategories[$template['id_category']]['name'],
                    'content' => json_decode($blocks[0]['json_content']),
                ];
            }
        }

        return new JsonResponse(
            [
                'message' => $message,
                'data' => $data,
            ]
        );
    }

    #[Route('/_blocek/icons/')]
    public function getIconsAction(Block $blockUtils): JsonResponse
    {
        $fontIcons = json_decode_strict(file_get_contents('web/build/icons-list.json'), true);

        $fileIcons = new \GlobIterator('data/icons/*.svg', \FilesystemIterator::CURRENT_AS_PATHNAME);
        $fileIcons = Mapping::mapKeys($fileIcons, fn ($fileName) => [basename('file-'.$fileName, '.svg'), '/'.$fileName]);

        return new JsonResponse(
            [
                'success' => true,
                'font_icons' => $fontIcons,
                'file_icons' => $fileIcons,
            ]
        );
    }

    #[Route('/_blocek/icons-inline/')]
    public function getIconsInlineAction(Block $blockUtils): JsonResponse
    {
        if (!findModule(\Modules::COMPONENTS)) {
            $fontIcons = json_decode_strict(file_get_contents('web/build/icons-list.json'), true);
        }

        $engineIcons = new \GlobIterator('engine/web/common/static/images/icons/*.svg', \FilesystemIterator::CURRENT_AS_PATHNAME);
        $shopIcons = new \GlobIterator('data/icons/*.svg', \FilesystemIterator::CURRENT_AS_PATHNAME);

        $fileIcons = [];

        // Apparently, its not necessary to strip doctype and xml tag, blocek lib will handle this.
        //        foreach ($engineIcons as $iconPath) {
        //            $icon = Icon::fromFile($iconPath);
        //            $fileIcons[basename($iconPath, '.svg')] = $icon->toHtml();
        //        }
        //        foreach ($shopIcons as $iconPath) {
        //            $icon = Icon::fromFile($iconPath);
        //            $fileIcons[basename($iconPath, '.svg')] = $icon->toHtml();
        //        }

        foreach ($engineIcons as $iconPath) {
            $fileIcons[basename($iconPath, '.svg')] = file_get_contents($iconPath);
        }
        foreach ($shopIcons as $iconPath) {
            $fileIcons[basename($iconPath, '.svg')] = file_get_contents($iconPath);
        }

        return new JsonResponse(
            [
                'success' => true,
                'font_icons' => $fontIcons ?? [],
                'file_icons' => $fileIcons,
            ]
        );
    }
}
