<?php

declare(strict_types=1);

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

namespace vonRotenberg\WeinanlieferungBundle\EventListener\DataContainer;

use Contao\CoreBundle\ServiceAnnotation\Callback;
use Contao\DataContainer;
use Contao\Date;
use Contao\StringUtil;
use Doctrine\DBAL\Connection;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlotsModel;

class WeinanlieferungReservationContainerListener
{
    /** @var Connection */
    protected $db;

    private $eventDispatcher;


    public function __construct(Connection $db, EventDispatcherInterface $eventDispatcher)
    {
        $this->db = $db;
        $this->eventDispatcher = $eventDispatcher;
    }


    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.sorten.options")
     */
    public function onSortenOptionsCallback(DataContainer $dc)
    {
        $arrSorten = [];

        if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null)
        {
            return $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 : '');
        }

        return $arrSorten;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.lage.options")
     */
    public function onLageOptionsCallback(DataContainer $dc)
    {
        $arrLagen = [];

        if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null || $Slot->lage === null)
        {
            return $arrLagen;
        }

        if (($Lagen = $Slot->getRelated('lage')) !== null)
        {
            $arrLagen = array_combine($Lagen->fetchEach('id'),$Lagen->fetchEach('title'));
        }

        return $arrLagen;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.ernteart.options")
     */
    public function onErnteartOptionsCallback(DataContainer $dc)
    {
        $arrErnteart = [];

        if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null || $Slot->ernteart === null)
        {
            return $arrErnteart;
        }

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

        return $arrErnteart;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.pid.options")
     */
    public function onPidOptionsCallback(DataContainer $dc)
    {
        $arrData = [];

        if (($CurrentSlot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) !== null)
        {
            $arrSorten = [];
            $intAvailableBehaelter = $CurrentSlot->getAvailableBehaelter();

            $Standort = $CurrentSlot->getRelated('pid');

            $Sorten = StringUtil::deserialize($CurrentSlot->sorten,true);
            foreach($Sorten as $sorte)
            {
                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']);
                $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']);
                $arrSorten[] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
            }
            $arrData[$CurrentSlot->id] = Date::parse(Date::getNumericDatimFormat(),$CurrentSlot->time) . ' - ' . ($Standort !== null  ? $Standort->title : $CurrentSlot->pid) . ' - (' . implode(', ',$arrSorten) . ') [Verfügbare Behälter: ' . $intAvailableBehaelter . '/' . $CurrentSlot->behaelter . ']';
        }

        if (($Slots = WeinanlieferungSlotsModel::findAllFuturePublished()) === null)
        {
            return $arrData;
        }

        foreach ($Slots as $slot)
        {
            $arrSorten = [];
            $intAvailableBehaelter = $slot->getAvailableBehaelter();

            $Standort = $slot->getRelated('pid');

            $Sorten = StringUtil::deserialize($slot->sorten,true);
            foreach($Sorten as $sorte)
            {
                $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']);
                $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']);
                $arrSorten[] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
            }
            $arrData[$slot->id] = Date::parse(Date::getNumericDatimFormat(),$slot->time) . ' - ' . ($Standort !== null  ? $Standort->title : $slot->pid) . ' - (' . implode(', ',$arrSorten) . ') [Verfügbare Behälter: ' . $intAvailableBehaelter . '/' . $slot->behaelter . ']';
        }

        return $arrData;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.uid.options")
     */
    public function onUidOptionsCallback(DataContainer $dc)
    {
        if (($Members = \MemberModel::findAll()) === null)
        {
            return [];
        }

        $arrData = [];
        foreach ($Members as $member)
        {
            $arrData[$member->id] = ($member->memberno !== null && $member->memberno ? $member->memberno.' ' : '') . $member->firstname . ' ' . $member->lastname . ' [' . $member->email . ']';
        }

        return $arrData;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.approved.save")
     */
    public function onApprovedSaveCallback($varValue, DataContainer $dc)
    {
        if (($Current = WeinanlieferungReservationModel::findByPk($dc->activeRecord->id)) !== null)
        {
            if ($Current->approved === '1' && $varValue !== '1')
            {
                $Current->approved_on = 0;
                $Current->save();
            } elseif ($Current->approved !== '1' && $varValue === '1')
            {
                $Current->approved_on = time();
                $Current->save();
            }
        }

        return $varValue;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.load")
     */
    public function onBehaelterNumbersLoadCallback($varValue, DataContainer $dc)
    {
        if (!empty($varValue))
        {
            $decodedValue = \json_decode($varValue, true);

            // Check if we have the new format (array of objects with behaelter and member)
            // or the old format (simple array of behaelter numbers)
            $isNewFormat = isset($decodedValue[0]) && is_array($decodedValue[0]) && isset($decodedValue[0]['behaelter']);

            if ($isNewFormat) {
                // The data is already in the correct format for the multiColumnWizard
                return $decodedValue;
            } else {
                // Convert old format to new format
                $result = [];

                // Get the member associated with this reservation as fallback
                $reservation = \vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel::findByPk($dc->id);
                $memberId = $reservation ? $reservation->uid : 0;
                $memberModel = \Contao\MemberModel::findById($memberId);
                $defaultMemberNo = $memberModel ? $memberModel->memberno : '';

                foreach ($decodedValue as $behaelterNumber) {
                    $result[] = [
                        'behaelter' => $behaelterNumber,
                        'member' => $defaultMemberNo
                    ];
                }

                return $result;
            }
        }
        return $varValue;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.save")
     */
    public function onBehaelterNumbersSaveCallback($varValue, DataContainer $dc)
    {
        $varValue = StringUtil::deserialize($varValue, true);
        if (!empty($varValue) && is_array($varValue))
        {
            // Get the reservation model
            $reservation = \vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel::findByPk($dc->id);
            if ($reservation === null) {
                throw new \Exception('Reservation not found');
            }

            // Get the slot model
            $slot = $reservation->getRelated('pid');
            if ($slot === null) {
                throw new \Exception('Slot not found');
            }

            // Check if the booking is in the past
            $isInPast = $slot->time < time();

            // Extract behaelter numbers from the multiColumnWizard data
            $behaelterNumbers = array_map(function($item) {
                return $item['behaelter'];
            }, $varValue);

            // Only perform validation if the booking is not in the past
            if (!$isInPast) {
                // Check if the number of container numbers matches the number of booked containers
                if (count($behaelterNumbers) != $reservation->behaelter) {
                    throw new \Exception('Die Anzahl der Behälternummern muss mit der Anzahl der gebuchten Behälter übereinstimmen.');
                }

                // Filter out the special value 9999 ("Nummer nicht bekannt") for duplicate check
                $numbersForDuplicateCheck = array_filter($behaelterNumbers, function($number) {
                    return $number !== '9999';
                });

                // Check for duplicate numbers (excluding the special value 9999)
                if (count(array_unique($numbersForDuplicateCheck)) != count($numbersForDuplicateCheck)) {
                    throw new \Exception('Jede Nummer kann nur einmal verwendet werden.');
                }

                // Get the standort to access the number_ranges
                $standort = $slot->getRelated('pid');
                if ($standort === null) {
                    throw new \Exception('Standort not found');
                }

                // Get all used numbers from current bookings (excluding past bookings)
                $usedNumbers = [];
                $currentTime = time();

                // Get the database connection
                $sql = "SELECT r.behaelter_numbers
                        FROM tl_vr_wa_reservation r
                        JOIN tl_vr_wa_slot s ON r.pid = s.id
                        WHERE r.behaelter_numbers != ''
                        AND s.time >= ?
                        AND r.id != ?";

                $stmt = $this->db->prepare($sql);
                $stmt->bindValue(1, $currentTime);
                $stmt->bindValue(2, $dc->id);
                $result = $stmt->executeQuery();

                while ($row = $result->fetchAssociative()) {
                    $numbers = json_decode($row['behaelter_numbers'], true);
                    if (is_array($numbers)) {
                        // Check if we have the new format (array of objects with behaelter and member)
                        // or the old format (simple array of behaelter numbers)
                        $isNewFormat = isset($numbers[0]) && is_array($numbers[0]) && isset($numbers[0]['behaelter']);

                        if ($isNewFormat) {
                            // Extract just the behaelter numbers from the new format
                            foreach ($numbers as $item) {
                                $usedNumbers[] = $item['behaelter'];
                            }
                        } else {
                            // Old format - simple array of behaelter numbers
                            foreach ($numbers as $number) {
                                $usedNumbers[] = $number;
                            }
                        }
                    }
                }

                // Get available numbers directly, excluding used ones
                $availableNumbers = $standort->extractNumbersFromRanges($usedNumbers);

                // Add the special option "9999" which is always valid
                $availableNumbers[] = '9999';

                // Check if all numbers are valid
                foreach ($numbersForDuplicateCheck as $number) {
                    if (!in_array($number, $availableNumbers)) {
                        throw new \Exception('Die Behälternummer "' . $number . '" ist nicht gültig oder wird bereits verwendet.');
                    }
                }
            }

            // Get the member associated with this reservation as fallback
            $memberId = $reservation->uid;
            $memberModel = \Contao\MemberModel::findById($memberId);
            $defaultMemberNo = $memberModel ? $memberModel->memberno : '';

            // Process the data from the multiColumnWizard
            $processedData = [];
            foreach ($varValue as $item) {
                $behaelterNumber = $item['behaelter'];

                // If member number is empty, use the default member number
                $memberNumber = !empty($item['member']) ? $item['member'] : $defaultMemberNo;

                $processedData[] = [
                    'behaelter' => $behaelterNumber,
                    'member' => $memberNumber
                ];
            }

            // Return the processed data as JSON
            return json_encode($processedData);
        }
        return $varValue;
    }

    /**
     * @Callback(table="tl_vr_wa_reservation", target="config.onsubmit")
     */
    public function onSubmitCallback(DataContainer $dc)
    {
        if (($reservation = WeinanlieferungReservationModel::findByPk($dc->id)) !== null && $reservation->checked_in)
        {
            $time = time();
            $reservation->checked_in = '1';
            $reservation->checked_in_on = $time;
            $reservation->save();

            $event = new CheckInCompletedEvent($reservation->row(), $reservation);
            $this->eventDispatcher->dispatch($event, CheckInCompletedEvent::NAME);
        }
    }

    /**
     * @Callback (table="tl_vr_wa_reservation", target="config.onload")
     */
    public function onLoadCallback(DataContainer $dc)
    {
        if ($_GET['act'] == 'edit' && ($reservation = WeinanlieferungReservationModel::findByPk($dc->id)) !== null && $reservation->checked_in_export_generated)
        {
            foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'] as &$field)
            {
                $field['eval']['disabled'] = true;
                $field['eval']['readonly'] = true;
                if (!empty($field['eval']['chosen']))
                {
                    $field['eval']['chosen'] = false;
                }
            }
        }
    }
}