<?php

namespace App\Http\Controllers\Owner;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Helpers\ResponseBuilder;
use App\Http\Helpers\ParamsValidator;
use App\Http\History\HistoryData;
use App\Http\History\HistoryTables;
use App\Modules\BookingTour\Application\BookingTourPassengerGrouperByGroup;
use App\Modules\BookingTour\Application\BookingTourPassengerLister;
use App\Modules\BookingTour\Domain\IdBookingTour;
use App\Modules\BookingTour\Infraestructure\BookingTourValidators;
use App\Modules\BookingTour\Infraestructure\MysqlBookingTourPassengerListRepository;
use App\Modules\BookingTourPassenger\Application\BookingTourPassengerNotificationCreator;
use App\Modules\BookingTourPassenger\Application\ValidateGroups\BookingTourPassengerGroupsValidationCommand;
use App\Modules\Group\Application\ValidatePassengers\GroupPassengersValidationCommand;
use App\Modules\HtlCampBooking\Application\CreateWithPassengers\CreateWithPassengersHtlCampBookingQuery;
use App\Modules\PackageRate\Domain\PackageRateType;
use App\Modules\Shared\Domain\Bus\Command\CommandBus;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Validator\Infraestructure\LaravelValidator;
use App\Modules\TourHtlCamp\Application\TourHtlCampFinder;
use App\Modules\TourHtlCamp\Domain\THCIdTour;
use App\Modules\TourHtlCamp\Infraestructure\MysqlTourHtlCampRepository;
use App\Providers\ExcelReport\Utils;
use Illuminate\Support\Facades\DB;

class BookingTour extends Controller
{
    private $oDomain;
    /** @var HistoryData */
    private $history;

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

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

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

    public function listBookingTourByGroup($Id_Booking, $BookingTour_Group)
    {
        return DB::select('CALL sp_booking_tour_list_by_group(?,?)', [$Id_Booking, $BookingTour_Group]);
    }

    public function update(Request $Request)
    {
        $oResponse  = array();
        $oValParams = BookingTourValidators::UPDATE;

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request($Request, $oValParams, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $this->history->obtainOld($Request->input('Id_BookingTour'));

            $oParam = [
                $Request->input("Id_BookingTour"),
                $Request->input("BookingTour_DateStart"),
                $Request->input("BookingTour_PickUp") . ':00',
                $Request->input("BookingTour_DateBriefing"),
                $Request->input("BookingTour_PlaceBriefing"),
                $Request->input("BookingTour_UseBookingPlaceBriefing"),
                $Request->input("Id_Booking"),
                $Request->header("Token")
            ];

            $tour = DB::select(
                'call sp_booking_tour_index(?)',
                [$Request->input("Id_BookingTour")]
            );

            $oData = DB::select('call sp_booking_tour_update(?,?,?,?,?,?,?,?)', $oParam);
            $tourUpdated = DB::select(
                'call sp_booking_tour_index(?)',
                [$Request->input("Id_BookingTour")]
            );
            if (count($tour) > 0) {
                if ($this->getDateStartText($tour) != $this->getDateStartText($tourUpdated)) {
                    $date = $Request->input("BookingTour_DateStart");
                    $token = $Request->header("Token");

                    $passengers = (new BookingTourPassengerLister(new MysqlBookingTourPassengerListRepository))(
                        'all',
                        new IdBookingTour($Request->input('Id_BookingTour')),
                        0,
                        ''
                    );

                    $this->bookingCreator->changeDate(
                        $Request->input('Id_BookingTour'),
                        $this->getDateStartText($tourUpdated),
                        $this->getDateStartText($tour)
                    );

                    $groups = $this->getGroupsFrom($passengers);
                    foreach ($passengers as $passenger) {
                        $this->commandBus->dispatch(new BookingTourPassengerGroupsValidationCommand($passenger->Id_BookingTourPassenger));
                    }
                    foreach ($groups as $group) {
                        $this->commandBus->dispatch(new GroupPassengersValidationCommand($group));
                    }

                    $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);
                    }
                }
            }

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $this->history->update(
                    $this->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->oDomain;
            $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 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));
    }

    public function addPackage(Request $Request)
    {
        $oResponse  = array();

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request($Request, BookingTourValidators::ADD_PACKAGE, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = [
                $Request->input('BookingTour_FeeType'),
                $Request->input('BookingTour_Full'),
                $Request->input("Id_Package"),
                $Request->input("Id_Booking"),
                $Request->input("BookingTour_Type"),
            ];
            $oData = DB::select('call sp_booking_tour_add_package(?,?,?,?,?)', $oParam);
            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $oParam = [
                    $Request->input("Id_Booking"),
                    $Request->input("Id_Package"),
                    $oData[0]->Response_Group
                ];
                DB::select('call sp_booking_tour_upgrade(?,?,?)', $oParam);

                $Tours = json_decode($Request->input("Tour_value"));

                for ($k = 0; $k < count($Tours); $k++) {
                    $oParam = [
                        $Tours[$k]->BookingTour_DateStart,
                        $Tours[$k]->Id_Tour,
                        $oData[0]->Response_Group,
                        $Request->input("Id_Booking")
                    ];
                    $oUpdateTous = DB::select('call sp_booking_tour_update_date(?,?,?,?)', $oParam);
                }
                $list = $this->listBookingTourByGroup($Request->input('Id_Booking'), $oData[0]->Response_Group);
                foreach ($list as $bookingTour) {
                    $this->updateBriefingDate($bookingTour);
                    $this->bookingCreator->addHtlCampBookings(
                        $bookingTour->Id_BookingTour,
                        $bookingTour->BookingTour_DateStart,
                        $bookingTour->Id_Tour
                    );
                    // $this->updateCampsDate($bookingTour);
                    $this->history->insert(
                        [$bookingTour],
                        $Request,
                        $Request->input('Id_Booking')
                    );
                }
            } else {
            }

            $oResponse["Response_Status"]           = $oData[0]->Response_Status;
            $oResponse["Response_Code"]             = $oData[0]->Response_Code;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $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 updateBriefingDate($bookingTour)
    {
        DB::select('CALL sp_booking_tour_update_date_briefing(?,@sts,@cod,@msg)', [$bookingTour->Id_BookingTour]);
    }

    private function updateCampsDate($bookingTour)
    {
        $tourCamps = $this->getBookingTourCamps($bookingTour);
        foreach ($tourCamps as $tourCamp) {
            $this->addBookingTourToSchedule($bookingTour, $tourCamp->Id_HtlCamp);
        }
    }

    public function getBookingTourCamps($bookingTour)
    {
        return (new TourHtlCampFinder(new MysqlTourHtlCampRepository))
            ->__invoke(new THCIdTour($bookingTour->Id_Tour), 'active')['Response_Data'];
    }

    public function addBookingTourToSchedule($bookingTour, $Id_HtlCamp)
    {
        $this->queryBus->ask(new CreateWithPassengersHtlCampBookingQuery(
            $bookingTour->BookingTour_DateStart . ' 00:00:00',
            $Id_HtlCamp,
            $bookingTour->Id_BookingTour,
            []
        ));
    }

    public function Change(Request $Request)
    {
        (new LaravelValidator)->validate($Request->all(), $this->oDomain, ['BookingTour_Value' => 'required|string']);
        $oResponse  = [];
        $continue = true;

        $BookingTour_Value = json_decode($Request->input("BookingTour_Value"));

        $bookingTours = [];

        foreach ($BookingTour_Value as $i => $BookingTour) {
            $this->history->obtainOld($BookingTour->Id_BookingTour);
            $bookingTours[$i] = [];
            $d = DB::select('call sp_booking_tour_index(?)', [$BookingTour->Id_BookingTour]);
            $bookingTours[$i][0] = DB::select('call sp_booking_tour_index(?)', [$BookingTour->Id_BookingTour])[0];

            $packageRate = new PackageRateType($BookingTour->PackageRate_Type);
            $oParam = array(
                $BookingTour->BookingTour_DateStart,
                $BookingTour->Id_PackageTour,
                $BookingTour->Id_BookingTour,
                $packageRate->value()
            );
            $oDataTour = DB::select('call sp_booking_tour_change_package(?,?,?,?)', $oParam);

            $bookingTours[$i][1] = DB::select('call sp_booking_tour_index(?)', [$BookingTour->Id_BookingTour])[0];

            if ($oDataTour[0]->Response_Success == 1) {
                $oParam     = array($BookingTour->Id_BookingTour);
                DB::select('CALL sp_booking_tour_add_items_from_package_tour(?,?)', [$BookingTour->Id_BookingTour, 1]);
            } else {
                $oData      = $oDataTour;
                $continue   = false;
                break;
            }
            $this->history->obtainNew($BookingTour->Id_BookingTour);
        }

        if ($continue == true) {
            $Path_Thumb     = config("var.PATH_PUBLIC") . config("var.USER_COUNTRY_THUMB");
            $oParam         = array(
                'all',
                $BookingTour_Value[0]->Id_BookingTour,
                0,
                $Path_Thumb
            );
            $oDataPax  = DB::select('call sp_booking_tour_passenger_list(?,?,?,?)', $oParam);
            for ($i = 0; $i < count($oDataPax); $i++) {
                $Id_BookingTour = $BookingTour_Value[0]->Id_BookingTour;
                $Id_Passenger = $oDataPax[$i]->Id_Passenger;
                $BookingTourPassenger_Total = count($oDataPax);
                $oParam = [
                    $Id_BookingTour,
                    $Id_Passenger,
                    $BookingTourPassenger_Total,
                    1
                ];
                $oDataTour  = DB::select('call sp_booking_tour_passenger_price(?,?,?,?)', $oParam);
                $oParam     = array($oDataPax[$i]->Id_Passenger, 1);
                $oDataTour  = DB::select('call sp_invoice_reload(?,?)', $oParam);
            }

            $this->updateBookingTours($Request, $bookingTours);
        }


        if ($continue == true) {
            if ($this->history->oldValue && count($this->history->oldValue)) {
                $this->history->update(
                    $this->history->oldValue[0]->Id_Booking,
                    $this->history->oldValue,
                    $this->history->newValueCache,
                    $Request
                );
            }
            $oData = array(
                array(
                    "Response_Status"   => 200,
                    "Response_Code"     => 200,
                    "Response_Message"  => "Paquete remplazado con éxito",
                    "Response_Reason"   => null
                )
            );

            $oData = json_decode(json_encode($oData));
        }

        $oResponse["Response_Status"]           = $oData[0]->Response_Status;
        $oResponse["Response_Code"]             = $oData[0]->Response_Code;
        $oResponse["Response_Domain"]           = $this->oDomain;
        $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);
    }

    public function updateBookingTours(Request $Request, array $bookingTours)
    {
        $notificationCreator = new BookingTourPassengerNotificationCreator($this->commandBus);
        Utils::compose(
            [$this, 'mapData'](
                Utils::compose(
                    [$this, 'mapData'](
                        Utils::compose(
                            [$notificationCreator, 'createNotificationWithMessage'](
                                $Request->header('Token'),
                                'ha sido cambiado de tour. Por favor verificar sus equipamentos.'
                            ),
                            fn ($btp) => $btp->Id_BookingTourPassenger
                        )
                    ),
                    fn ($idBookingTour) => DB::select(
                        'call sp_booking_tour_passenger_list(?,?,?,?)',
                        ['all', $idBookingTour, 0, '']
                    ),
                    fn ($bts) => $bts[1]->Id_BookingTour
                )
            ),
            [$this, 'filterDifferentTour']
        )($bookingTours);
    }

    public function mapData(callable $funct)
    {
        return function (array $data) use ($funct) {
            return array_map($funct, $data);
        };
    }

    public function filterDifferentTour(array $bookingTours)
    {
        return array_filter($bookingTours, fn ($bts) => $bts[0]->Id_Tour != $bts[1]->Id_Tour);
    }

    public function filterDifferentDate(array $bookingTours)
    {
        array_filter(
            $bookingTours,
            function ($bts) {
                return $bts[0]->Id_Tour == $bts[0]->Id_Tour &&
                    $bts[0]->BookingTour_DateStart != $bts[1]->BookingTour_DateStart;
            }
        );
    }

    public function index(Request $Request, $Id)
    {
        $oResponse  = array();

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Id($Id, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = array($Id);
            $oData  = DB::select('call sp_booking_tour_index(?)', $oParam);

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $oResponse["Response_Message"]          = $this->oDomain . " Index";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }

        return ResponseBuilder::Response($oResponse);
    }

    public function Delete(Request $Request, $Id)
    {
        $oResponse  = array();
        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Id($Id, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $this->history->obtainOld($Id);
            $oParam = array($Id);
            $oData  = DB::select('call sp_booking_tour_delete(?)', $oParam);


            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $Path_Thumb     = config("var.PATH_PUBLIC") . config("var.USER_COUNTRY_THUMB");
                $oParam         = array(
                    'all',
                    $Id,
                    0,
                    $Path_Thumb
                );

                $oDataPax  = DB::select('call sp_booking_tour_passenger_list(?,?,?,?)', $oParam);

                for ($i = 0; $i < count($oDataPax); $i++) {

                    $oParam = array(
                        $oDataPax[$i]->Id_BookingTourPassenger,
                        1,
                        1
                    );
                    $oData  = DB::select('call sp_booking_tour_passenger_status(?,?,?)', $oParam);
                    $data = (new BookingTourPassengerNotificationCreator($this->commandBus))
                        ->__invoke($oDataPax[$i]->Id_BookingTourPassenger, $Request->header('Token'));

                    $oParam     = array($oDataPax[$i]->Id_Passenger, 1);
                    $oDataTour  = DB::select('call sp_invoice_reload(?,?)', $oParam);
                }
                $this->history->delete(
                    self::findBooking($Id),
                    $this->history->oldValue,
                    $Request
                );
            } else {
            }

            $oResponse["Response_Status"]           = $oData[0]->Response_Status;
            $oResponse["Response_Code"]             = $oData[0]->Response_Code;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $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);
    }

    public function Enable($Id)
    {
        $oResponse  = array();
        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Id($Id, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $this->history->obtainOld($Id);
            $oParam = array($Id);
            $oData  = DB::select('call sp_booking_tour_enable(?)', $oParam);

            $oResponse["Response_Status"]           = $oData[0]->Response_Status;
            $oResponse["Response_Code"]             = $oData[0]->Response_Code;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $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);
    }

    public function Report(Request $Request, $Id)
    {
        $oResponse  = array();

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Id($Id, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = array($Id);

            $oData = array_map(
                function ($tour) {
                    $getEquipments  = function ($val) {
                        $val->Pax = DB::select('call sp_booking_equipment_passenger_list(?,?)', [$val->Id_BookingEquipment, 0]);
                        return $val;
                    };
                    $getExtras      = function ($val) {
                        $val->Pax = DB::select('call sp_booking_extra_passenger_list(?,?)', [$val->Id_BookingExtra, 0]);
                        return $val;
                    };
                    $getUpgrades    = function ($val) {
                        $val->Pax = DB::select('call sp_booking_upgrade_passenger_list(?,?)', [$val->Id_BookingUpgrade, 0]);
                        return $val;
                    };

                    $id = [$tour->Id_BookingTour];

                    $tour->Passengers   = DB::select('call sp_booking_tour_passenger_list(?,?,?,?)', ['all', $tour->Id_BookingTour, 0, '']);
                    $tour->Equipments   = array_map(
                        $getEquipments,
                        DB::select('call sp_booking_equipment_list(?)', $id)
                    );
                    $tour->Extras       = array_map(
                        $getExtras,
                        DB::select('call sp_booking_extra_list(?)', $id)
                    );
                    $tour->Upgrades     = array_map(
                        $getUpgrades,
                        DB::select('call sp_booking_upgrade_list(?)', $id)
                    );
                    return $tour;
                },
                DB::select('call sp_booking_tour_index(?)', [$Id])
            );

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $oResponse["Response_Message"]          = $this->oDomain . " Index";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }

        return ResponseBuilder::Response($oResponse);
    }
}
