<?php

declare(strict_types=1);

/*
 * This file is part of contao-weinanlieferung-bundle.
 *
 * (c) vonRotenberg
 *
 * @license commercial
 */

namespace vonRotenberg\WeinanlieferungBundle\Controller\Backend;

use Contao\Ajax;
use Contao\Backend;
use Contao\BackendTemplate;
use Contao\CheckBox;
use Contao\Config;
use Contao\Controller;
use Contao\CoreBundle\Controller\AbstractController;
use Contao\CoreBundle\Csrf\ContaoCsrfTokenManager;
use Contao\Date;
use Contao\DC_File;
use Contao\DC_Table;
use Contao\Environment;
use Contao\FileTree;
use Contao\FrontendUser;
use Contao\Image;
use Contao\Input;
use Contao\Message;
use Contao\SelectMenu;
use Contao\StringUtil;
use Contao\System;
use Contao\TextArea;
use Contao\TextField;
use Contao\Upload;
use Contao\Widget;
use Doctrine\DBAL\Connection;
use MenAtWork\MultiColumnWizardBundle\Contao\Widgets\MultiColumnWizard;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment as TwigEnvironment;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlotsModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlottypesModel;

/**
 * @Route("%contao.backend.route_prefix%/weinanlieferung/slotassistant", name=self::class, defaults={"_scope" = "backend"})
 */
class WeinanlieferungSlotAssistantController extends AbstractController
{
    private $twig;
    private $tokenManager;
    private $request;
    private $db;

    private $translator;

    protected $fields;

    public function __construct(TwigEnvironment $twig, ContaoCsrfTokenManager $tokenManager, RequestStack $requestStack, Connection $db, TranslatorInterface $translator)
    {
        $this->twig = $twig;
        $this->tokenManager = $tokenManager;
        $this->request = $requestStack->getCurrentRequest();
        $this->db = $db;
        $this->translator = $translator;

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


        $strKey = Input::get('popup') ? 'popupReferer' : 'referer';
        $strRefererId = $this->request->attributes->get('_contao_referer_id');

        $session = $objSession->get($strKey);
        $session[$strRefererId]['current'] = substr(Environment::get('requestUri'), \strlen(Environment::get('path')) + 1);
        $objSession->set($strKey, $session);

        Controller::loadDataContainer('tl_vr_wa_slot');
        System::loadLanguageFile('tl_vr_wa_slot');
    }

    public function __invoke(): Response
    {
        System::loadLanguageFile('default');
        System::loadLanguageFile('tl_vr_wa_slotassistant');
        Controller::loadDataContainer('tl_vr_wa_slot');
        Controller::loadDataContainer('tl_vr_wa_slotassistant');

        $GLOBALS['TL_CSS']['cirrus'] = 'bundles/vonrotenbergweinanlieferung/css/backend.css|static';
        $arrData = [
            'request_token' => $this->tokenManager->getDefaultTokenValue(),
            'ref'           => $this->request->attributes->get('_contao_referer_id')
        ];

        // Handle ajax request pre actions
        if ($_POST && Environment::get('isAjaxRequest'))
        {
            $this->objAjax = new Ajax(Input::post('action'));
            $this->objAjax->executePreActions();
        }

        // Form submitted
        if (Input::post('FORM_SUBMIT') == 'wa_slotassistant')
        {
            $this->generateSlots();
        }

        $arrData['fields'] = $this->getWidgets();
        $arrData['messages'] = Message::generateUnwrapped() . Backend::getSystemMessages();

        // Handle ajax request post actions
        if ($_POST && Environment::get('isAjaxRequest'))
        {
            $dc = new DC_File('tl_vr_wa_slotassistant');
            $this->objAjax->executePostActions($dc);
        }

        return new Response(
            $this->twig->render(
                '@Contao_VonrotenbergWeinanlieferungBundle/be_wa_slotassistant.html.twig',
                $arrData
            )
        );
    }

    protected function getWidgets()
    {
        if (!empty($this->fields))
        {
            return $this->fields;
        }

        $arrWidgets = [
            'base_legend' => [
                [
                    'class'  => 'w50',
                    'help'   => 'Legen Sie hier den Zeitslot-Typ/Annahmestelle fest.',
                    'widget' => new SelectMenu([
                        'name'      => 'slotType',
                        'mandatory' => true,
                        'options'   => $this->getSlottypeOptions(),
                        'label'     => 'Zeitslot-Typ',
                    ])
                ],
                [
                    'class'  => 'w50',
                    'help'   => 'Die Pufferzeit zwischen den Slots.',
                    'widget' => new TextField([
                        'name'      => 'buffer',
                        'mandatory' => true,
                        'label'     => 'Pufferzeit in min.',
                        'rgxp'      => 'natural'
                    ])
                ]
            ],
            'time_legend' => [
                /*[
                    'class'  => 'w50 wizard',
                    'help'   => 'Der Zeitpunkt ab wann der erste Zeitslot angelegt werden soll.',
                    'widget' => new TextField([
                        'name'       => 'date_start[1]',
                        'mandatory'  => true,
                        'label'      => 'Zeitraum Beginn',
                        'rgxp'       => 'datim',
                        'datepicker' => true,
                    ])
                ],
                [
                    'class'  => 'w50 wizard',
                    'help'   => 'Der Zeitpunkt bis wann der letzte Zeitslot abgeschlossen sein muss.',
                    'widget' => new TextField([
                        'name'       => 'date_end[1]',
                        'mandatory'  => true,
                        'label'      => 'Zeitraum Ende',
                        'rgxp'       => 'datim',
                        'datepicker' => true,
                    ])
                ],*/
                [
                    'class'  => 'clr',
                    'help' => 'Die Zeiträume, für die neue Slots angelegt werden sollen.',
                    'widget' => new MultiColumnWizard([
                        'name'       => 'timeframe',
                        'label'      => 'Zeiträume',
                        'minCount' => 1,
                        'columnFields' =>
                            [
                                'date_start' =>
                                    [
                                        'label' => $GLOBALS['TL_LANG']['tl_vr_wa_slotassistant']['date_start'],
                                        'inputType' => 'text',
                                        'eval' => [
                                            'style' => 'width:150px',
                                            'mandatory' => true,
                                            'rgxp' =>'datim',
                                            'datepicker' => true
                                        ]
                                    ],
                                'date_end' =>
                                    [
                                        'label' => $GLOBALS['TL_LANG']['tl_vr_wa_slotassistant']['date_end'],
                                        'inputType' => 'text',
                                        'eval' => [
                                            'style' => 'width:150px',
                                            'mandatory' => true,
                                            'rgxp' =>'datim',
                                            'datepicker' => true
                                        ]
                                    ]
                            ]
                    ])
                ]
            ],
            'slot_legend' => [
                /*[
                  'class'    => 'clr',
                  'help'     => 'Wählen Sie die Attribute aus, die für diesen Slot gelten sollen.',
                  'widget'   => new CheckBox([
                      'name'        => 'attributes',
                      'mandatory'   => false,
                      'label'       => 'Attribute',
                      'options_callback'        => array('vonRotenberg\WeinanlieferungBundle\EventListener\DataContainer\WeinanlieferungSlotContainerListener', 'getAttributeOptions'),
                  ])
                ],*/
                [
                    'class'    => 'clr',
                    'help'     => 'Wählen Sie die Attribute aus, die für diesen Slot gelten sollen.',
                    'widget'   => new CheckBox(CheckBox::getAttributesFromDca($GLOBALS['TL_DCA']['tl_vr_wa_slot']['fields']['attributes'],'attributes',null,'Attribute','tl_vr_wa_slot',new DC_Table('tl_vr_wa_slot')))
                ],
                [
                    'class'  => 'w50 wizard',
                    'help'   => 'Der Beginn des Zeitraums, ab wann der Slot im Vorfeld gebucht werden kann.',
                    'widget' => new TextField([
                        'name'      => 'bookableFrom',
                        'mandatory' => true,
                        'label'     => 'Buchbar ab',
                        'rgxp'      => 'datim',
                        'datepicker' => true
                    ])
                ],
                [
                    'class'  => 'w50',
                    'help'   => 'Das Ende des Zeitraums, bis wann der Slot im Vorfeld gebucht werden kann.',
                    'widget' => new TextField([
                        'name'      => 'bookableTill',
                        'mandatory' => true,
                        'label'     => 'Buchbar bis in h',
                        'rgxp'      => 'natural'
                    ])
                ],
                [
                    'class'  => 'clr',
                    'help'   => 'Anmerkungen zum Zeitslot. Diese werden bei der Buchung angezeigt.',
                    'widget' => new TextArea([
                        'name'      => 'annotation',
                        'mandatory' => false,
                        'label'     => 'Anmerkungen',
                        'rte'       => 'tinyMCE'
                    ])
                ],
                [
                    'class'  => 'clr',
                    'help'   => 'Anmerkungen zum Zeitslot. Diese werden bei der Buchung angezeigt.',
                    'widget' => new FileTree([
                        'name'        => 'enclosure',
                        'mandatory'   => false,
                        'label'       => 'Anmerkungen',
                        'multiple'    => true,
                        'fieldType'   => 'checkbox',
                        'filesOnly'   => true,
                        'isDownloads' => true
                    ])
                ]
            ]
        ];


        foreach ($arrWidgets as $group => $fields)
        {
            foreach ($fields as $field)
            {
                if (empty($field['widget']->id))
                {
                    $field['widget']->id = $field['widget']->name;
                }
                if (empty($field['widget']->table))
                {
                    $field['widget']->strTable = 'tl_vr_wa_slotassistant';
                }

                $wizard = '';

                // Date picker
                if ($field['widget']->datepicker ?? null)
                {
                    $rgxp = $field['widget']->rgxp ?? 'date';
                    $format = Date::formatToJs(Config::get($rgxp . 'Format'));

                    switch ($rgxp)
                    {
                        case 'datim':
                            $time = ",\n        timePicker: true";
                            break;

                        case 'time':
                            $time = ",\n        pickOnly: \"time\"";
                            break;

                        default:
                            $time = '';
                            break;
                    }

                    $strOnSelect = '';

                    // Trigger the auto-submit function (see #8603)
                    if ($field['widget']->submitOnChange ?? null)
                    {
                        $strOnSelect = ",\n        onSelect: function() { Backend.autoSubmit(\"wa_slotassistant\"); }";
                    }

                    $wizard .= ' ' . Image::getHtml('assets/datepicker/images/icon.svg', '', 'title="' . StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['datepicker']) . '" id="toggle_' . $field['widget']->id . '" style="cursor:pointer"') . '
  <script>
    window.addEvent("domready", function() {
      new Picker.Date($("ctrl_' . $field['widget']->id . '"), {
        draggable: false,
        toggle: $("toggle_' . $field['widget']->id . '"),
        format: "' . $format . '",
        positionOffset: {x:-211,y:-209}' . $time . ',
        pickerClass: "datepicker_bootstrap",
        useFadeInOut: !Browser.ie' . $strOnSelect . ',
        startDay: ' . $GLOBALS['TL_LANG']['MSC']['weekOffset'] . ',
        titleFormat: "' . $GLOBALS['TL_LANG']['MSC']['titleFormat'] . '"
      });
    });
  </script>';
                    $field['widget']->wizard = $wizard;
                }

                // Replace the textarea with an RTE instance
                if (!empty($field['widget']->rte))
                {
                    list($file, $type) = explode('|', $field['widget']->rte, 2) + [null, null];

                    $fileBrowserTypes = [];
                    $pickerBuilder = System::getContainer()->get('contao.picker.builder');

                    foreach (['file' => 'image', 'link' => 'file'] as $context => $fileBrowserType)
                    {
                        if ($pickerBuilder->supportsContext($context))
                        {
                            $fileBrowserTypes[] = $fileBrowserType;
                        }
                    }

                    $objTemplate = new BackendTemplate('be_' . $file);
                    $objTemplate->selector = 'ctrl_' . $field['widget']->id;
                    $objTemplate->type = $type;
                    $objTemplate->fileBrowserTypes = $fileBrowserTypes;
                    $objTemplate->source = 'wa_slotassistant.' . $field['widget']->id;
                    $objTemplate->readonly = (bool)($field['widget']->readonly ?? false);

                    // Deprecated since Contao 4.0, to be removed in Contao 5.0
                    $objTemplate->language = Backend::getTinyMceLanguage();

                    $field['widget']->updateMode = $objTemplate->parse();

                    unset($file, $type, $pickerBuilder, $fileBrowserTypes, $fileBrowserType);
                }
            }
        }

        $this->fields = $arrWidgets;
        return $this->fields;
    }

    protected function getSlottypeOptions()
    {
        $arrOptions = [['value'=>'','label'=>'-']];
        $Slottypes = $this->db->prepare("SELECT t.id, t.title, t.containers, t.duration, t.`default`, s.title as 'standort' FROM tl_vr_wa_slottypes t INNER JOIN tl_vr_wa_standort s ON s.id = t.pid ORDER BY t.`default` DESC, t.title")->executeQuery();

        $arrOptGroups = [];
        if ($Slottypes->rowCount() > 0)
        {
            foreach ($Slottypes->fetchAllAssociative() as $option)
            {
                $arrOptGroups[$option['standort']][] = [
                    'value' => $option['id'],
                    'label' => $option['title'].' ['.$this->translator->trans('MSC.wa_container', [], 'contao_tl_vr_wa_units').': '.$option['containers'].'] ['.$this->translator->trans('MSC.wa_duration', [], 'contao_tl_vr_wa_units').': '.$option['duration'].' '.($option['duration'] == 1 ? $this->translator->trans('MSC.wa_duration_minute', [], 'contao_tl_vr_wa_units') : $this->translator->trans('MSC.wa_duration_minutes', [], 'contao_tl_vr_wa_units')).'] ['.$option['standort'].']' . ($option['default'] ? ' (Standard)' : '')
                ];
            }
        }

        ksort($arrOptGroups);
        $arrOptions = array_merge($arrOptions, $arrOptGroups);

        return $arrOptions;
    }

    protected function validateForm()
    {
        $blnSubmit = false;
        if (Input::post('FORM_SUBMIT') == 'wa_slotassistant')
        {
            $blnSubmit = true;
            foreach ($this->getWidgets() as $group => $fields)
            {
                foreach ($fields as $field)
                {
                    $field['widget']->validate();
                    if ($field['widget']->hasErrors())
                    {
                        $blnSubmit = false;
                    }
                }
            }
        }
        return $blnSubmit;
    }

    protected function generateSlots()
    {
        if (!Input::post('FORM_SUBMIT') == 'wa_slotassistant' || !$this->validateForm())
        {
            return;
        }

        if (
            ($SlotType = WeinanlieferungSlottypesModel::findByPk(Input::post('slotType'))) === null
            || ($Standort = $SlotType->getRelated('pid')) === null
            || !\is_array(Input::post('timeframe'))
        )
        {
            return;
        }

        $now = time();
        $intNewSlots = 0;
        $intBuffer = intval(Input::post('buffer')) * 60;
        $intDuration = (int) $SlotType->duration*60;

        // Get timespans
        foreach (Input::post('timeframe') as $timeframe)
        {
            if (
                !isset($timeframe['date_start'])
                || !isset($timeframe['date_end'])
            )
            {
                continue;
            }

            $StartDate = new Date($timeframe['date_start'], Date::getNumericDatimFormat());
            $EndDate = new Date($timeframe['date_end'], Date::getNumericDatimFormat());


            $intStart  = $StartDate->tstamp;
            $intEnd  = $EndDate->tstamp;
            $arrTimes = [];
            $arrStartTimes = [];
            $arrEndTimes = [];

            // Get existing slots in the timeframe
            $ExistingSlots = $this->db->prepare("SELECT id, date, time as 'start', (time+duration*60-1) as 'end', duration FROM tl_vr_wa_slot WHERE pid = ? AND (time >= ? OR time+$intDuration+$intBuffer >= ?) AND (time <= ? OR time+$intDuration+$intBuffer <= ?) ORDER BY time")
                                      ->executeQuery([$Standort->id, $intStart, $intStart, $intEnd, $intEnd]);

            if ($ExistingSlots->rowCount())
            {
                foreach ($ExistingSlots->fetchAllAssociative() as $slot)
                {
                    $arrTimes[$slot['start']] = [
                        'start' => $slot['start'],
                        'end'  => $slot['end']
                    ];
                    $arrStartTimes[] = $slot['start'];
                    $arrEndTimes[] = $slot['end'];
                }
            }

            // Create all possible timeslots for given timeframe
            $intTime = $intStart;
            while ($intTime <= $intEnd-$intDuration)
            {
                $arrConflicts = [];

                foreach ($arrStartTimes as $check_start)
                {
                    $checkslot = $arrTimes[$check_start];
                    if (
                        (
                            $intTime >= $checkslot['start']
                            && $intTime <= $checkslot['end']
                        )
                        || (
                            $intTime + $intDuration + $intBuffer > $checkslot['start']
                            && $intTime + $intDuration + $intBuffer < $checkslot['end']
                        )
                    ) {
                        $arrConflicts[] = $checkslot['end'];
                    }
                }

                if (\count($arrConflicts) > 0)
                {
                    $intTime = max($arrConflicts) + (max($intBuffer,1));
                    continue;
                }


                $NewDateTime = new Date($intTime);
                $NewSlot = new WeinanlieferungSlotsModel();
                $bookableFrom = new Date(Input::post('bookableFrom'), Date::getNumericDatimFormat());
                $NewSlot->setRow([
                    'pid' => $SlotType->pid,
                    'tstamp' => $now,
                    'date' => $NewDateTime->dayBegin,
                    'time' => $intTime,
                    'duration' => $intDuration/60,
                    'behaelter' => $SlotType->containers,
                    'overcapacity' => $SlotType->overcapacity,
                    'anmerkungen' => Input::post('annotation'),
                    'attributes' => \implode(',',\is_array(Input::post('attributes')) ? Input::post('attributes') : []),
                    'buchbar_ab' => (int) $bookableFrom->tstamp,
                    'buchbar_bis' => (int) $intTime-intval(Input::post('bookableTill'))*3600,
                    'published' => '1'
                ]);

                if (Input::post('enclosure'))
                {
                    $NewSlot->addEnclosure = '1';
                    $NewSlot->enclosure = explode(',',Input::post('enclosure'));
                }

                $NewSlot->save();
                $intNewSlots++;

                $intTime = $intTime + $intDuration + $intBuffer;

            }
        }

        Message::addConfirmation(sprintf('Es wurden %s neue Zeitslots angelegt.',$intNewSlots));
        Backend::reload();
    }
}