<?php

declare(strict_types=1);

/*
 * This file is part of modal bundle for Contao.
 *
 * (c) Benjamin Roth
 *
 * @license LGPL-3.0-or-later
 */

namespace vonRotenberg\ModalBundle\EventListener\DataContainer;

use Contao\Backend;
use Contao\BackendUser;
use Contao\CoreBundle\Exception\AccessDeniedException;
use Contao\CoreBundle\ServiceAnnotation\Callback;
use Contao\DataContainer;
use Contao\Image;
use Contao\Input;
use Contao\StringUtil;
use Contao\System;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Bundle\SecurityBundle\Security;
use vonRotenberg\ModalBundle\Security\ModalPermissions;

class ModalDataContainerListener
{
    private Security $security;
    private SessionInterface $session;

    public function __construct(Security $security, Request $request)
    {
        $this->security = $security;
        $this->session = $request->getSession();
    }

    /**
     * @Callback(table="tl_vr_modal", target="config.onload")
     */
    public function checkPermission(DataContainer $dc = null): void
    {
        $user = $this->security->getUser();
        $userId = $user instanceof BackendUser ? (int)$user->id : 0;

        $objSession = System::getContainer()->get('session');
        $session = $objSession->all();

        if ($user->isAdmin)
        {
            return;
        }

        // Set root IDs
        if (empty($user->modals) || !is_array($user->modals))
        {
            $root = array(0);
        }
        else
        {
            $root = $user->modals;
        }

        if (is_array($session['CURRENT']['IDS'] ?? null))
        {
            $edit_all = array();
            $delete_all = array();

            foreach ($session['CURRENT']['IDS'] as $id)
            {
                if ($this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $id))
                {
                    $edit_all[] = $id;
                }

                if ($this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS, $id))
                {
                    $delete_all[] = $id;
                }
            }

            $session['CURRENT']['IDS'] = (Input::get('act') == 'deleteAll') ? $delete_all : $edit_all;
        }

        // Set allowed clipboard IDs
        if (!empty($session['CLIPBOARD']['tl_vr_modal']['id']) && is_array($session['CLIPBOARD']['tl_vr_modal']['id']))
        {
            $clipboard = array();

            foreach ($session['CLIPBOARD']['tl_vr_modal']['id'] as $id)
            {
                if ($this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $id))
                {
                    $clipboard[] = $id;
                }
            }

            $session['CLIPBOARD']['tl_article']['id'] = $clipboard;
        }

        // Overwrite the session
        $objSession->replace($session);

        // Check permissions to add modals
        if (!$this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS))
        {
            $GLOBALS['TL_DCA']['tl_vr_modal']['config']['closed'] = true;
            $GLOBALS['TL_DCA']['tl_vr_modal']['config']['notCreatable'] = true;
            $GLOBALS['TL_DCA']['tl_vr_modal']['config']['notCopyable'] = true;
        }

        // Check permissions to delete modals
        if (!$this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS))
        {
            $GLOBALS['TL_DCA']['tl_vr_modal']['config']['notDeletable'] = true;
        }

        // Check current action
        switch (Input::get('act'))
        {
            case 'select':
                // Allow
                break;

            case 'edit':
            case 'show':
                if (!in_array(Input::get('id'), $root))
                {
                    throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' form ID ' . Input::get('id') . '.');
                }
                break;

            case 'editAll':
            case 'overrideAll':
                $session = $objSession->all();

                $session['CURRENT']['IDS'] = array_intersect((array) $session['CURRENT']['IDS'], $root);

                $objSession->replace($session);
                break;

            case 'copy':
            case 'create':
                if (!$this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS))
                {
                    throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' modals.');
                }
                break;

            case 'delete':
                if (!$this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS))
                {
                    throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' modal ID ' . Input::get('id') . '.');
                }
                break;

            case 'deleteAll':
            case 'copyAll':
                $session = $this->session->all();

                if (Input::get('act') == 'deleteAll' && !$this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS))
                {
                    $session['CURRENT']['IDS'] = array();
                } else
                {
                    if (Input::get('act') == 'copyAll' && !$this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS))
                    {
                        $session['CURRENT']['IDS'] = array();
                    }
                }
                $this->session->replace($session);
                break;

            default:
                if (Input::get('act'))
                {
                    throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' modals.');
                }
                break;
        }
    }

    /**
     * @Callback(table="tl_vr_modal", target="config.oncreate")
     * @Callback(table="tl_vr_modal", target="config.oncopy")
     */
    public function adjustPermissions($insertId)
    {
        $user = $this->security->getUser();
        $userId = $user instanceof BackendUser ? (int)$user->id : 0;

        // The oncreate_callback passes $insertId as second argument
        if (func_num_args() == 4)
        {
            $insertId = func_get_arg(1);
        }

        if ($user->isAdmin)
        {
            return;
        }

        // Set root IDs
        if (empty($user->modals) || !is_array($user->modals))
        {
            $root = array(0);
        }
        else
        {
            $root = $user->modals;
        }

        // The form is enabled already
        if (in_array($insertId, $root))
        {
            return;
        }

        /** @var AttributeBagInterface $objSessionBag */
        $objSessionBag = System::getContainer()->get('session')->getBag('contao_backend');
        $arrNew = $objSessionBag->get('new_records');

        if (is_array($arrNew['tl_vr_modal']) && in_array($insertId, $arrNew['tl_vr_modal']))
        {
            /** @var Connection $db */
            $db = System::getContainer()->get('database_connection');

            // Add the permissions on group level
            if ($user->inherit != 'custom')
            {
                $objGroup = $db->executeQuery("SELECT id, modals, modalp FROM tl_user_group WHERE id IN(" . implode(',', array_map('\intval', $user->groups)) . ")");

                foreach ($objGroup->fetchAllAssociative() as $group)
                {
                    $arrModalp = StringUtil::deserialize($group['modalp']);

                    if (is_array($arrModalp) && in_array('create', $arrModalp))
                    {
                        $arrModals = StringUtil::deserialize($group['modals'], true);
                        $arrModals[] = $insertId;

                        $db->prepare("UPDATE tl_user_group SET modals=? WHERE id=?")
                            ->executeQuery([serialize($arrModals), $group['id']]);
                    }
                }
            }

            // Add the permissions on user level
            if ($user->inherit != 'group')
            {
                $objUser = $db->prepare("SELECT modals, modalp FROM tl_user WHERE id=? LIMIT 1")
                    ->executeQuery([$user->id]);

                $arrModalp = StringUtil::deserialize($objUser->fetchOne());

                if (is_array($arrModalp) && in_array('create', $arrModalp))
                {
                    $arrModals = StringUtil::deserialize($objUser->modals, true);
                    $arrModals[] = $insertId;

                    $db->prepare("UPDATE tl_user SET modals=? WHERE id=?")
                        ->executeQuery([serialize($arrModals), $user->id]);
                }
            }

            // Add the new element to the user object
            $root[] = $insertId;
            $user->forms = $root;
        }
    }

    /**
     * @Callback(table="tl_vr_modal", target="list.operations.copy.button")
     */
    public function copyModal($row, $href, $label, $title, $icon, $attributes): string
    {
        return $this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
    }

    /**
     * @Callback(table="tl_vr_modal", target="list.operations.delete.button")
     */
    public function deleteModal($row, $href, $label, $title, $icon, $attributes): string
    {
        return $this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
    }

    /**
     * @Callback(table="tl_vr_modal", target="list.operations.edit.button")
     */
    public function editModal($row, $href, $label, $title, $icon, $attributes): string
    {
        return $this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $row['id']) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
    }

    /**
     * @Callback(table="tl_vr_modal", target="list.operations.editheader.button")
     */
    public function editHeaderModal($row, $href, $label, $title, $icon, $attributes): string
    {
        return $this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $row['id']) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
    }
}