<?php

namespace App\Modules\Room\Application\Update;

use App\Modules\Admin\Domain\IdAdmin;
use App\Modules\BookingExtraPassenger\Application\UpdateStatus\UpdateStatusBookingExtraPassengerQuery;
use App\Modules\Provider\Domain\IdProvider;
use App\Modules\Room\Application\Index\IndexRoomPopulate;
use App\Modules\Room\Domain\RoomStatus;
use App\Modules\Shared\Domain\Bus\Query\QueryHandler;
use App\Modules\Room\Application\Index\RoomIndexer;
use App\Modules\Room\Domain\IdRoom;
use App\Modules\Room\Domain\Room;
use App\Modules\Room\Domain\RoomBuyCode;
use App\Modules\Room\Domain\RoomBuyPrice;
use App\Modules\Room\Domain\RoomDateChanged;
use App\Modules\Room\Domain\RoomLock;
use App\Modules\Room\Domain\RoomObservation;
use App\Modules\Room\Domain\RoomRequestDateEnd;
use App\Modules\Room\Domain\RoomRequestDateStart;
use App\Modules\Room\Domain\RoomReservationDateFinal;
use App\Modules\Shared\Domain\Aggregate\AggregateRoot;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Exceptions\BasicRequestException;
use App\Modules\TypePayment\Domain\IdTypePayment;
use App\Modules\TypeRoom\Domain\IdTypeRoom;
use Exception;

final class UpdateRoomQueryHandler implements QueryHandler
{
    public function __construct(
        private RoomUpdater $updater,
        private RoomIndexer $indexer,
        private QueryBus $queryBus,
    ) {}

    private function conditionalSet($condition, AggregateRoot &$aggregateRoot, $setter, $object)
    {
        if (isset($condition)) {
            $aggregateRoot->$setter(new $object($condition));
        }
    }

    public function __invoke(UpdateRoomQuery $query): UpdateRoomQueryResponse
    {
        /** @var Room $model */
        $model = $this->indexer->__invoke(new IdRoom($query->id), new IndexRoomPopulate(''));

        // VALIDACION PARA ROOM DATE CHANGED      
        $currentDateChanged = $model->dateChanged()->value();
        $valueDateChanged   = $this->setDateChangedValidation($currentDateChanged, $query->dateChanged);

        if (isset($valueDateChanged)) {
            $model->setDateChanged($valueDateChanged);
        }

        if ($this->isLocked($model)) {
            if (isset($query->idTypeRoom) && $model->idTypeRoom()->value() != $query->idTypeRoom) {
                throw new BasicRequestException(400, 400, "El tipo de habitación no puede ser actualizada");
            }
        }

        $set = [
            [$query->requestDateStart, $model, 'setRequestDateStart', RoomRequestDateStart::class],
            [$query->requestDateEnd, $model, 'setRequestDateEnd', RoomRequestDateEnd::class],
            [$query->reservationDateFinal, $model, 'setReservationDateFinal', RoomReservationDateFinal::class],
            [$query->observation, $model, 'setObservation', RoomObservation::class],
            [$query->buyPrice, $model, 'setBuyPrice', RoomBuyPrice::class],
            [$query->buyCode, $model, 'setBuyCode', RoomBuyCode::class],
            [$query->status, $model, 'setStatus', RoomStatus::class],
            [$query->idTypeRoom, $model, 'setIdTypeRoom', IdTypeRoom::class],
            [$query->idProvider, $model, 'setIdProvider', IdProvider::class],
            [$query->idTypePayment, $model, 'setIdTypePayment', IdTypePayment::class],
            [$query->idAdmin, $model, 'setIdAdmin', IdAdmin::class],
        ];
        foreach ($set as $value) {
            $this->conditionalSet($value[0], $value[1], $value[2], $value[3]);
        }

        $response = $this->updater->__invoke(
            $model,
        );

        // ACTUALIZAR BOOKING EXTRA PASSENGER STATUS
        $bookingExtraPassenger_Status = 1;
        $statusRoom = $query->status;

        if ($statusRoom == 2 || $statusRoom == 3) {
            $bookingExtraPassenger_Status = 1;
        }
        if ($statusRoom == 4 || $statusRoom == 5) {
            $bookingExtraPassenger_Status = 2;
        }

        $responseUpdateStatus = $this->queryBus->ask(new UpdateStatusBookingExtraPassengerQuery(
            $query->id,
            $bookingExtraPassenger_Status
        ));

        return $response;
    }

    private function isLocked(Room $value)
    {
        return $value->lock()->value() == RoomLock::VALUE_TRUE;
    }

    public function setDateChangedValidation($currentDateChanged, $dateChanged)
    {
        if ($currentDateChanged === RoomDateChanged::VALUE_TRUE) {
            if (!isset($dateChanged)) {
                throw new Exception('La fecha fue actualizada. Actualice la página.');
                // throw new Exception('La fecha fue cambiada antes. Para cambiar este estado, envíe el parámetro Room_DateChanged.');
            } else {
                return new RoomDateChanged($dateChanged);
            }
        } else {
            if (isset($dateChanged)) {
                throw new Exception('La fecha aún no fue cambiada antes, por ello no puede actualizar el estado de Room_DateChanged.');
            }
        }
        return null;
    }
}
