<?php

namespace App\Http\Controllers\Owner;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Owner\Passenger\PassengerUpdater;
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\Infraestructure\BookingTourValidators;
use App\Modules\BookingTourPassenger\Application\BookingTourPassengerNotificationCreator;
use App\Modules\HtlCampBooking\Application\CreateWithPassengers\CreateWithPassengersHtlCampBookingQuery;
use App\Modules\HtlCampBooking\Application\List\ListHtlCampBookingQuery;
use App\Modules\HtlCampBooking\Application\List\ListHtlCampBookingResponse;
use App\Modules\HtlCampBooking\Application\List\ListHtlCampBookingResponses;
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\List\ListTourHtlCampQuery;
use App\Modules\TourHtlCamp\Application\List\ListTourHtlCampResponse;
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 PassengerUpdater $passengerUpdater,
        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 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()
            );
            /** @var ListHtlCampBookingResponses $htlCampBooking */
            $htlCampBooking = $this->queryBus->ask(new ListHtlCampBookingQuery($BookingTour->Id_BookingTour, 'active'));
            $oDataTour = DB::select('call sp_booking_tour_change_package(?,?,?,?)', $oParam);

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

            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->passengerUpdater->updateLockHtlCampBookingPassenger($oDataPax[$i]->Id_Passenger);
            }

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

    /**
     * @param ListHtlCampBookingResponse[] $tourHtlCamps
     */
    private function repareCamps(array $htlCampBookings, $bookingTour)
    {
        /** @var ListTourHtlCampResponse $tourHtlCamps */
        $tourHtlCamps = $this->queryBus->ask(new ListTourHtlCampQuery($bookingTour->Id_Tour, 'active'));

        foreach ($tourHtlCamps as $tourHtlCamp) {
            $found = null;
            foreach ($htlCampBookings as $htlCampBooking) {
                if ($htlCampBooking->Id_HtlCamp == $tourHtlCamp->Id_HtlCamp) {
                    $found = $htlCampBooking;
                }
            }
            if ($found == null) {
                $this->addHtlCampBooking($bookingTour, $tourHtlCamp);
            } else {
                $this->updateCampsDate($bookingTour, $found);
            }
        }

        foreach ($htlCampBookings as $htlCampBooking) {
            $found = null;
            foreach ($tourHtlCamps as $tourHtlCamp) {
                if ($htlCampBooking->Id_HtlCamp == $tourHtlCamp->Id_HtlCamp) {
                    $found = $tourHtlCamp;
                }
            }
            if ($found == null) {
                $this->deleteHtlCampBooking($htlCampBooking);
            }
        }
    }

    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_values(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->passengerUpdater->updateLockHtlCampBookingPassenger($oDataPax[$i]->Id_Passenger);
                }
                $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);
    }
}
