<?php

namespace App\Http\Controllers\Owner\BookingTour;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Owner\BookingCreator;
use App\Http\Controllers\Owner\BookingTour\Dto\BookingTourUpdateDTO;
use App\Http\Controllers\Owner\c_Admin_Session;
use App\Http\Helpers\ResponseBuilder;
use App\Http\History\HistoryData;
use App\Http\History\HistoryTables;
use App\Modules\BookingAdditional\Application\CreateFromBookingExtraPassenger\CreateFromBookingExtraPassengerBookingAdditionalQuery;
use App\Modules\BookingAdditional\Application\CreateFromBookingUpgradePassenger\CreateFromBookingUpgradePassengerBookingAdditionalQuery;
use App\Modules\BookingTour\Application\BookingTourPassengerGrouperByGroup;
use App\Modules\BookingTour\Application\BookingTourPassengerLister;
use App\Modules\BookingTour\Application\BookingTourResponse;
use App\Modules\BookingTour\Application\Index\IndexBookingTourQuery;
use App\Modules\BookingTour\Application\UpdateDate\UpdateDateBookingTourQuery;
use App\Modules\BookingTour\Application\UpdateItemDates\UpdateItemDatesBookingTourQuery;
use App\Modules\BookingTour\Domain\IdBookingTour;
use App\Modules\BookingTour\Infraestructure\MysqlBookingTourPassengerListRepository;
use App\Modules\BookingTourPassenger\Application\ValidateGroups\BookingTourPassengerGroupsValidationCommand;
use App\Modules\Group\Application\ValidatePassengers\GroupPassengersValidationCommand;
use App\Modules\Shared\Application\InsertHandler;
use App\Modules\Shared\Domain\Bus\Command\CommandBus;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Infraestructure\Requester;
use App\Modules\Shared\Validator\Infraestructure\LaravelDTOGenerator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class BookingTourUpdateController extends Controller
{

    private string $domain = 'Booking Tour';
    private HistoryData $history;

    public function __construct(
        private CommandBus $commandBus,
        private BookingCreator $bookingCreator,
        private QueryBus $queryBus,
        private LaravelDTOGenerator $dtoGenerator
    ) {
        $this->history = new HistoryData(HistoryTables::BOOKING_TOUR);
    }

    public function __invoke(Request $request)
    {
        $adminSession = c_Admin_Session::check($request);
        $idAdmin = $adminSession['Id_Admin'];

        /** @var BookingTourUpdateDTO $dto */
        $dto = $this->dtoGenerator->generate($request->all(), $this->domain, BookingTourUpdateDTO::class);

        $this->history->obtainOld($request->input('Id_BookingTour'));
        $tour = [$this->queryBus->ask(new IndexBookingTourQuery($dto->Id_BookingTour))];

        /** @var UpdateDateBookingTourResponse $response */
        $response = $this->queryBus->ask(new UpdateDateBookingTourQuery(
            $dto->Id_BookingTour,
            $dto->Id_Booking,
            $dto->BookingTour_DateBriefing,
            $dto->BookingTour_DateStart,
            $dto->BookingTour_PickUp,
            $dto->BookingTour_PlaceBriefing,
            $dto->BookingTour_UseBookingPlaceBriefing,
            $dto->BookingTour_SupportEmail,
            $dto->BookingTour_SupportPassword,
            $idAdmin
        ));
        // /** @var TourLockResponses */
        // $response = $this->queryBus(new ListTourLockQuery($tour[0]->Id_Tour, $tour[0]->BookingTour_DateStart));

        // if ($response->isLocked()) {
        //     dd($response->data());
        // }

        $oParam = [
            $dto->Id_BookingTour,
            $dto->BookingTour_DateStart,
            $dto->BookingTour_PickUp,
            $dto->BookingTour_DateBriefing,
            $dto->BookingTour_PlaceBriefing,
            $dto->BookingTour_UseBookingPlaceBriefing,
            $dto->Id_Booking,
            $request->header("Token")
        ];

        $oData = DB::select('call sp_booking_tour_update(?,?,?,?,?,?,?,?)', $oParam);
        $tourUpdated = [$this->queryBus->ask(new IndexBookingTourQuery($dto->Id_BookingTour))];

        // $this->queryBus->ask(new UpdateItemDatesBookingTourQuery($dto->Id_BookingTour, $this->getDateStartText($tour)));

        if (count($tour) > 0 && $oData[0]->Response_Success == 1) {
            $formDateUpdated = $this->getDateStartText($tourUpdated);
            $formDateTour = $this->getDateStartText($tour);
            $date = $request->input("BookingTour_DateStart");
            $token = $request->header("Token");

            if ($formDateTour != $formDateUpdated) {
                $this->updateItemsDates($dto->Id_BookingTour, $formDateTour);
                // $this->insertItems($dto->Id_BookingTour, $)
                $this->bookingCreator->changeDate($request->input('Id_BookingTour'), $formDateUpdated, $formDateTour);
                $passengers = $this->getPassengers($request->input('Id_BookingTour'));
                if ($request->input('List_Extra_Refer')) {
                    $this->insertExtras($request->input('List_Extra_Refer'), $request->input('Id_BookingTour'), $passengers);
                }
                if ($request->input('List_Upgrade_Refer')) {
                    $this->insertUpgrades($request->input('List_Upgrade_Refer'), $request->input('Id_BookingTour'), $passengers);
                }
                $groups = $this->getGroupsFrom($passengers);
                $this->updatePassengersInGroups($passengers, $groups);
                $this->insertNotifications($passengers, $date, $token);
            }
        }

        //  RESPONSE
        if ($oData[0]->Response_Success == 1) {
            $this->history->update(
                self::findBooking($request->input("Id_BookingTour")),
                $this->history->oldValue,
                [$request->all()],
                $request
            );
        }
        $oResponse["Response_Status"]           = $oData[0]->Response_Status;
        $oResponse["Response_Code"]             = $oData[0]->Response_Code;
        $oResponse["Response_Domain"]           = $this->domain;
        $oResponse["Response_Message"]          = $oData[0]->Response_Message;
        $oResponse["Response_Data"]             = null;
        $oResponse["Response_Error_Message"]    = $oData[0]->Response_Message;
        $oResponse["Response_Error_Reason"]     = $oData[0]->Response_Reason;


        return ResponseBuilder::Response($oResponse);
    }

    private function insertExtras($listExtras, $idBookingTour, $passengers)
    {
        foreach ($listExtras as $extra) {
            $quantity = count($passengers);
            $feature = '';
            $show = 2;
            $included = 1;
            $mode = 1;
            $idProvider = 0;
            $print = 1;

            $response = (new InsertHandler())->handle(DB::select(
                'call sp_booking_extra_insert(?,?,?,?,?,?,?,?,?,?)',
                [$quantity, $feature, $show, $included, $mode, $idBookingTour, $idProvider, $extra['Id_Extra'], $print, 1]
            ));

            foreach ($passengers as $passenger) {
                $oParam = [
                    $response[0]->Response_Id,
                    $passenger->Id_BookingTourPassenger,
                    1
                ];
                $dataPax = (new InsertHandler())->handle(DB::select('call sp_booking_extra_passenger_insert(?,?,?)', $oParam));
                $this->queryBus->ask(new CreateFromBookingExtraPassengerBookingAdditionalQuery($dataPax[0]->Response_Id));
            }
            Requester::requestUpdate(
                'CALL sp_internal_booking_extra_update_dates(?, @sts, @cod, @msg)',
                [$response[0]->Response_Id]
            );
        }
    }

    private function insertUpgrades($listUpgrades, $idBookingTour, $passengers)
    {
        foreach ($listUpgrades as $upgrade) {
            $quantity = count($passengers);
            $feature = '';
            $show = 2;
            $included = 1;
            $mode = 1;
            $idProvider = 0;
            $print = 1;

            $response = (new InsertHandler())->handle(DB::select(
                'call sp_booking_upgrade_insert(?,?,?,?,?,?,?,?,?,?)',
                [$quantity, $feature, $show, $included, $mode, $idBookingTour, $idProvider, $upgrade['Id_Upgrade'], $print, 1]
            ));

            foreach ($passengers as $passenger) {
                $oParam = [
                    $response[0]->Response_Id,
                    $passenger->Id_BookingTourPassenger,
                    1
                ];
                $oDataPax = (new InsertHandler())->handle(DB::select('call sp_booking_upgrade_passenger_insert(?,?,?)', $oParam));
                $this->queryBus->ask(new CreateFromBookingUpgradePassengerBookingAdditionalQuery($oDataPax[0]->Response_Id));
            }
            Requester::requestUpdate(
                'CALL sp_internal_booking_upgrade_update_dates(?, @sts, @cod, @msg)',
                [$response[0]->Response_Id]
            );
        }
    }

    private function insertNotifications($passengers, $date, $token)
    {
        $notifications = (new BookingTourPassengerGrouperByGroup)($passengers, $date);

        foreach ($notifications as $not) {
            $oParam = [$not->Notification_Description, 3, $not->Id, $token, 1];
            DB::select('call sp_notification_insert(?,?,?,?,?)', $oParam);
        }
    }

    private function getPassengers($idBookingTour)
    {
        return (new BookingTourPassengerLister(new MysqlBookingTourPassengerListRepository))(
            'all',
            new IdBookingTour($idBookingTour),
            0,
            ''
        );
    }

    private function updatePassengersInGroups($passengers, $groups)
    {
        foreach ($passengers as $passenger) {
            $this->commandBus->dispatch(new BookingTourPassengerGroupsValidationCommand($passenger->Id_BookingTourPassenger));
        }
        foreach ($groups as $group) {
            $this->commandBus->dispatch(new GroupPassengersValidationCommand($group));
        }
    }

    private function updateItemsDates($idBookingTour, $dateStart)
    {
        $this->queryBus->ask(new UpdateItemDatesBookingTourQuery($idBookingTour, $dateStart));
    }

    public static function findBooking($idBookingTour)
    {
        $found = DB::select('CALL sp_booking_tour_index(?)', [$idBookingTour]);

        if (count($found)) {
            return $found[0]->Id_Booking;
        } else {
            return null;
        }
    }

    private function getGroupsFrom($passengers)
    {
        $groupIds = [];
        foreach ($passengers as $passenger) {
            if ($passenger->Id_Group == null) {
                continue;
            }
            $groupList = explode('||', $passenger->Id_Group);
            foreach ($groupList as $groupId) {
                $groupIds[$groupId] = true;
            }
        }
        return array_keys($groupIds);
    }

    private function getDateStartText($tour)
    {
        return date('Y-m-d', strtotime($tour[0]->BookingTour_DateStart));
    }
}
