<?php

declare(strict_types=1);

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

namespace vonRotenberg\WeinanlieferungBundle\Controller\Frontend\Ajax;

use Contao\Controller;
use Contao\CoreBundle\Controller\AbstractController;
use Contao\CoreBundle\Exception\PageNotFoundException;
use Contao\CoreBundle\Framework\ContaoFramework;
use Contao\CoreBundle\Security\Authentication\Token\TokenChecker;
use Contao\Environment;
use Contao\File;
use Contao\FilesModel;
use Contao\FormFileUpload;
use Contao\Frontend;
use Contao\FrontendUser;
use Contao\Input;
use Contao\StringUtil;
use Contao\System;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlotsModel;

/**
 * @Route("/_ajax/vr_wa/v1/slot", name="vr_wa_slot_ajax", defaults={"_scope" = "frontend", "_token_check" = false})
 */
class SlotAjaxController extends AbstractController
{
    private $tokenChecker;
    private $translator;
    private $framework;

    public function __construct(ContaoFramework $framework, TokenChecker $tokenChecker, TranslatorInterface $translator)
    {
        $this->framework = $framework;
        $this->tokenChecker = $tokenChecker;
        $this->translator = $translator;
    }

    public function __invoke(Request $request)
    {
        System::loadLanguageFile('default');

        if (!$this->tokenChecker->hasFrontendUser())
        {
            return $this->renderUnauthorized();
        }

        if (empty($_REQUEST['do']))
        {
            return new Response('Required parameter missing',412);
        }

        $blnModal = true;

        if (!empty($_REQUEST['modal']))
        {
            $blnModal = !(strtolower($_REQUEST['modal']) == 'false');
        }

        switch ($_REQUEST['do'])
        {
            case 'details':
                return $this->renderDetails($blnModal);

            case 'annotation':
                return $this->renderAnnotation($blnModal);

            case 'booking':
                return $this->renderBooking($blnModal);

            case 'reservate':
                return $this->reservate();

            case 'updateReservation':
                return $this->updateReservation();

            case 'delete':
                return $this->deleteReservation();
        }

        return new Response('',500);
    }

    protected function renderDetails(bool $blnModal=true,string $error=null)
    {
        if (empty($_REQUEST['id']))
        {
            return new Response('Required parameter missing',412);
        }

        if (($Slot = WeinanlieferungSlotsModel::findPublishedById($_REQUEST['id'])) === null)
        {
            return new Response('Could not load slot data',500);
        }

        // Get slot reservations from user
        $arrReservations = [];

        if (($Reservations = WeinanlieferungReservationModel::findBy(["uid = ?","pid = ?"],[FrontendUser::getInstance()->id,$Slot->id])) !== null)
        {
            foreach ($Reservations as $reservation)
            {
                $arrSortenBooked = [];
                $SortenLeseart = explode(';',$reservation->sorten);
                foreach($SortenLeseart as $sorteLeseart)
                {
                    list($sorte,$leseart) = explode(',',$sorteLeseart);
                    $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte);
                    $objLeseart = WeinanlieferungLeseartModel::findByPk($leseart);
                    $arrSortenBooked[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
                }
                /*if (($Sorten = $reservation->getRelated('sorten')) !== null)
                {
                    $arrSortenBooked = $Sorten->fetchEach('title');
                }*/
                $arrReservations[] = array_merge($reservation->row(),[
                    'sorten' => $arrSortenBooked
                ]);
            }
        }

        // Build data
        $arrSorten = [];
        $Sorten = StringUtil::deserialize($Slot->sorten,true);
        foreach($Sorten as $sorte)
        {
            $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']);
            $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']);
            $arrSorten[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
        }
        /*if (($Sorten = $Slot->getRelated('sorte')) !== null)
        {
            $arrSorten = array_combine($Sorten->fetchEach('id'),$Sorten->fetchEach('title'));
        }*/

        $arrErnteart = [];
        if ($Slot->ernteart !== null)
        {
            foreach (explode(',', $Slot->ernteart) as $ernteart)
            {
                $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
            }
        }

        $intAvailableBehaelter = $Slot->getAvailableBehaelter();

        $arrData = [
            'modal' => $blnModal,
            'id'       => $Slot->id,
            'slot'     => array_merge($Slot->row(),[
                'sorte' => $arrSorten,
                'behaelterAvailable' => $intAvailableBehaelter
            ]),
            'standort' => $Slot->getRelated('pid'),
            'lage' => $Slot->getRelated('lage'),
            'ernteart' => $arrErnteart,
            'buchen' => [
                'buchbar' => (boolean) $intAvailableBehaelter,
                'behaelter' => range(min($intAvailableBehaelter,1),$intAvailableBehaelter),
                'sorten' => $arrSorten
            ],
            'reservations' => $arrReservations
        ];

        if (!empty($error))
        {
            $arrData['toast'] = $error;
        }

        return $this->render('@Contao/modal_slot_details.html.twig',$arrData);
    }

    protected function renderAnnotation(bool $blnModal=true,string $error=null)
    {
        if (empty($_REQUEST['id']))
        {
            return new Response('Required parameter missing',412);
        }

        if (($Slot = WeinanlieferungSlotsModel::findPublishedById($_REQUEST['id'])) === null)
        {
            return new Response('Could not load slot data',500);
        }

        // Build data
        $arrSorten = [];
        $Sorten = StringUtil::deserialize($Slot->sorten,true);
        foreach($Sorten as $sorte)
        {
            $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']);
            $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']);
            $arrSorten[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
        }

        $arrErnteart = [];
        if ($Slot->ernteart !== null)
        {
            foreach (explode(',', $Slot->ernteart) as $ernteart)
            {
                $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
            }
        }

        $intAvailableBehaelter = $Slot->getAvailableBehaelter();

        $arrData = [
            'modal' => $blnModal,
            'id'       => $Slot->id,
            'slot'     => array_merge($Slot->row(),[
                'sorte' => $arrSorten,
                'behaelterAvailable' => $intAvailableBehaelter
            ]),
            'standort' => $Slot->getRelated('pid'),
            'lage' => $Slot->getRelated('lage'),
            'ernteart' => $arrErnteart,
            'buchen' => [
                'buchbar' => (boolean) $intAvailableBehaelter,
                'behaelter' => range(min($intAvailableBehaelter,1),$intAvailableBehaelter),
                'sorten' => $arrSorten
            ],
        ];

        return $this->render('@Contao/modal_slot_annotation.html.twig',$arrData);
    }

    protected function renderBooking(bool $blnModal=true)
    {
        $arrData = [];

        if (empty($_REQUEST['id']))
        {
            return new Response('Required parameter missing',412);
        }

        /** @var WeinanlieferungSlotsModel $Slot */
        if (($Booking = WeinanlieferungReservationModel::findById($_REQUEST['id'])) === null || ($Slot = $Booking->getRelated('pid')) === null)
        {
            return new Response('Could not load booking data',500);
        }

        $objFile = FilesModel::findByUuid($Booking->upload);

        if (!empty($_REQUEST['deleteFile']) && $_REQUEST['deleteFile'] && $objFile !== null)
        {
            $File = new File($objFile->path);
            if ($File->delete())
            {
                $objFile->delete();
                $objFile = null;
            }
        }

        if (!empty($Booking->upload) && $objFile !== null)
        {
            $File = new File($objFile->path);
            $strHref = Environment::get('request');

            // Remove an existing file parameter (see #5683)
            if (isset($_GET['file']))
            {
                $strHref = preg_replace('/(&(amp;)?|\?)file=[^&]+/', '', $strHref);
            }

            $strHref .= (strpos($strHref, '?') !== false ? '&amp;' : '?') . 'file=' . System::urlEncode($File->value);

            $arrData['file'] = [
                'link' => $strHref,
                'filename' => $File->filename,
                'extension' => $File->extension,
                'name' => $File->name,
                'path' => $File->dirname
            ];
        }

        // Send the file to the browser (see #4632 and #8375)
        if ($objFile !== null && ($file = Input::get('file', true)))
        {
            if ($file == $objFile->path)
            {
                Controller::sendFileToBrowser($file);
            }
        }

        $arrSortenAvailable = [];
        $Sorten = StringUtil::deserialize($Slot->sorten,true);
        foreach($Sorten as $sorte)
        {
            $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']);
            $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']);
            $arrSortenAvailable[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
        }
        $arrSortenBooked = [];
        $SortenLeseart = explode(';',$Booking->sorten);
        foreach($SortenLeseart as $sorteLeseart)
        {
            list($sorte,$leseart) = explode(',',$sorteLeseart);
            $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte);
            $objLeseart = WeinanlieferungLeseartModel::findByPk($leseart);
            $arrSortenBooked[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
        }

        $intAvailableBehaelter = $Slot->getAvailableBehaelter();

        $arrData = array_merge($arrData,[
            'modal' => $blnModal,
            'id'       => $Booking->id,
            'slot'     => array_merge($Slot->row(),[
                'sorte' => $arrSortenAvailable,
                'behaelterAvailable' => $intAvailableBehaelter
            ]),
            'buchung' => array_merge($Booking->row(),[
                'sorten' => $arrSortenBooked
            ]),
            'standort' => $Slot->getRelated('pid'),
            'buchen' => [
                'buchbar' => (boolean) $intAvailableBehaelter,
                'behaelter' => range(min($intAvailableBehaelter,1),$intAvailableBehaelter+$Booking->behaelter),
                'sorten' => $arrSortenAvailable
            ]
        ]);

        return $this->render('@Contao/modal_booking_details.html.twig',$arrData);
    }

    protected function reservate()
    {
        Controller::loadDataContainer('tl_vr_wa_reservation');
        $arrData = [];

        if (($rootPage = Frontend::getRootPageFromUrl()) !== null && !empty($rootPage->vr_wa_uploadFolderSRC))
        {
            $File = new FormFileUpload(\Contao\Widget::getAttributesFromDca($GLOBALS['TL_DCA']['tl_vr_wa_reservation']['fields']['upload'], 'upload'));
            $File->storeFile = true;
            $File->doNotOverwrite = true;
            $File->uploadFolder = $rootPage->vr_wa_uploadFolderSRC;

            $File->validate();

            if ($File->hasErrors())
            {
                return $this->renderDetails(false,'<div class="toast toast--danger mx-0">' . $File->getErrorAsHTML() . '</div>');
            }

            if (!empty($_SESSION['FILES'][$File->name]))
            {
                $arrData['filename'] = $_SESSION['FILES'][$File->name]['name'] ?? '';
                $arrData['upload'] = $_SESSION['FILES'][$File->name]['uuid'] ? StringUtil::uuidToBin($_SESSION['FILES'][$File->name]['uuid']) : null;
            }
        }

        if (empty($_REQUEST['id']) || empty(Input::post('behaelter')) || empty(Input::post('sorten')))
        {
            return new Response('Missing parameter',412);
        }

        $arrSorten = [];
        if (!is_array(Input::post('sorten')))
        {
            $arrSorten[] = Input::post('sorten');
        } else {
            $arrSorten = implode(';', Input::post('sorten'));
        }

        $Reservation = new WeinanlieferungReservationModel();
        $arrData = array_merge($arrData,[
            'pid' => $_REQUEST['id'],
            'tstamp' => time(),
            'uid' => FrontendUser::getInstance()->id,
            'behaelter' => Input::post('behaelter'),
            'sorten' => $arrSorten
        ]);
        $Reservation->setRow($arrData);


        $Reservation->save();

        return new Response('<div class="toast toast--success mx-0"><p>Reservierung erfolgreich</p></div>',200,['HX-Trigger'=> 'updateWaList']);
    }

    protected function updateReservation()
    {
        Controller::loadDataContainer('tl_vr_wa_reservation');

        if (empty($_REQUEST['id']) || empty(Input::post('behaelter')) || empty(Input::post('sorten')))
        {
            return new Response('Missing parameter',412);
        }

        if (($Reservation = WeinanlieferungReservationModel::findById($_REQUEST['id'])) === null)
        {
            return new Response('Could not load booking data',500);
        }

        if (($rootPage = Frontend::getRootPageFromUrl()) !== null && !empty($rootPage->vr_wa_uploadFolderSRC))
        {
            $File = new FormFileUpload(\Contao\Widget::getAttributesFromDca($GLOBALS['TL_DCA']['tl_vr_wa_reservation']['fields']['upload'], 'upload'));
            $File->storeFile = true;
            $File->doNotOverwrite = true;
            $File->uploadFolder = $rootPage->vr_wa_uploadFolderSRC;

            $File->validate();

            if ($File->hasErrors())
            {
                return $this->renderDetails(false,'<div class="toast toast--danger mx-0">' . $File->getErrorAsHTML() . '</div>');
            }

            if (!empty($_SESSION['FILES'][$File->name]))
            {
                $Reservation->filename = $_SESSION['FILES'][$File->name]['name'] ?? '';
                $Reservation->upload = $_SESSION['FILES'][$File->name]['uuid'] ? StringUtil::uuidToBin($_SESSION['FILES'][$File->name]['uuid']) : null;
            }
        }

        $arrSorten = [];
        if (!is_array(Input::post('sorten')))
        {
            $arrSorten[] = Input::post('sorten');
        } else {
            $arrSorten = implode(';', Input::post('sorten'));
        }

        $Reservation->tstamp = time();
        $Reservation->behaelter = Input::post('behaelter');
        $Reservation->sorten = $arrSorten;

        $Reservation->save();

        return new Response('<div class="toast toast--success mx-0"><p>Reservierung erfolgreich geändert</p></div>',200,['HX-Trigger'=> 'updateWaBooking']);
    }

    protected function deleteReservation()
    {
        if (empty($_REQUEST['id']))
        {
            return new Response('Missing parameter',412);
        }

        /** @var Connection $db */
        $db = Controller::getContainer()->get('database_connection');

        $arrCriteria = [
            'uid' => FrontendUser::getInstance()->id,
            'id' => $_REQUEST['id']
        ];

        if ($db->delete('tl_vr_wa_reservation',$arrCriteria))
        {
            return new Response(null,203,['HX-Trigger'=> 'updateWaBooking']);
        }

        return new Response('Could not delete',500);
    }

    protected function renderUnauthorized()
    {
        return $this->render('@Contao/modal_unauthorized.html.twig');
    }

}