<?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\BookingTourPassenger\Application\BookingTourPassengerNotificationCreator;
use App\Modules\BookingTourPassenger\Application\Index\IndexBookingTourPassengerQuery;
use App\Modules\BookingTourPassenger\Application\Index\IndexBookingTourPassengerResponse;
use App\Modules\BookingTourPassenger\Domain\BookingTourPassengerStatus;
use App\Modules\BookingTourPassenger\Infraestructure\BookingTourPassengerValidators;
use App\Modules\Shared\Domain\Bus\Command\CommandBus;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Infraestructure\RequestChecker;
use App\Modules\Shared\Infraestructure\Requester;
use App\Modules\Shared\Validator\Infraestructure\LaravelValidator;
use Illuminate\Support\Facades\DB;
use Throwable;

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

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

    public function Insert(Request $Request)
    {
        $oSucces    = true;
        $oResponse  = array();
        $oValParams = BookingTourPassengerValidators::INSERT;

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $BookingTour = json_decode($Request->input("BookingTours"));
            foreach ($BookingTour as $k => $bookingTourElement) {
                $paxAmount = count($bookingTourElement->Pax);
                $Id_BookingTour = $bookingTourElement->Id;
                foreach ($bookingTourElement->Pax as $paxInfo) {
                    $camps = $this->bookingCreator->getHtlCampBookings($Id_BookingTour);
                    $oData_TourPassenger = $this->insertBookingTourPassenger($Id_BookingTour, $paxInfo, $paxAmount, $camps->data());

                    if ($oData_TourPassenger[0]->Response_Success == 0) {
                        $oSucces = false;
                        $oData = $oData_TourPassenger;
                        break;
                    }

                    RequestChecker::requestUpdate(
                        'CALL `sp_internal_booking_item_passenger_insert_required`(?,?, @sts, @cod, @msg)',
                        [$oData_TourPassenger[0]->Response_Id, $bookingTourElement->Id]
                    );

                    $oParam         = [$paxInfo->Id, 1];
                    $oData_Invoice  = DB::select('call sp_invoice_reload(?,?)', $oParam);
                    DB::select('call sp_passenger_check_info_pay(?)', [$paxInfo->Id]);
                    $this->passengerUpdater->updateLockHtlCampBookingPassenger($paxInfo->Id);
                    if ($oData_Invoice[0]->Response_Success == 0) {
                        $oSucces = false;
                        $oData = $oData_Invoice;
                        break 2;
                    }

                    RequestChecker::requestUpdate(
                        'CALL sp_internal_booking_tour_update_item_dates_by_passenger(?, ?, @sts, @cod, @msg)',
                        [$bookingTourElement->Id, $paxInfo->Id]
                    );
                    DB::select('call sp_invoice_reload(?,?)', $oParam);

                    $this->history->insert(
                        [$oParam],
                        $Request,
                        $Request->input('Id_Booking')
                    );
                }
            }

            if ($oSucces == true) {
                $oData = array(
                    array(
                        "Response_Status"   => 200,
                        "Response_Code"     => 200,
                        "Response_Message"  => "Pasajeros guardados 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);
    }

    private function insertBookingTourPassenger($Id_BookingTour, $bookingTourInfo, $paxAmount, $camps)
    {
        $oParam = [
            $Id_BookingTour,
            $bookingTourInfo->Id,
            $paxAmount
        ];
        $response = DB::select('call sp_booking_tour_passenger_insert(?,?,?)', $oParam);
        array_map(fn($camp) => $this->bookingCreator->addHtlCampBookingPassenger(
            $camp->Id_HtlCampBooking,
            $response[0]->Response_Id
        ), $camps);
        return $response;
    }

    private function canChangeTo($index, $status)
    {
        return BookingTourPassengerStatus::changeFromTo($index->BookingTourPassenger_Status, $status);
    }

    private function buildRequestError($message)
    {
        return ResponseBuilder::Response([
            'Response_Status'           => 406,
            'Response_Code'             => 406,
            'Response_Domain'           => $this->oDomain,
            'Response_Message'          => $message,
            'Response_Data'             => null,
            'Response_Error_Message'    => $message,
            'Response_Error_Reason'     => $message,
        ]);
    }

    // public function StatusChange(Request $request)
    // {
    //     (new LaravelValidator())->validate($request->all(), $this->oDomain, BookingTourPassengerValidators::STATUS);

    //     $idBookingTourPassenger = $request->input('Id_BookingTourPassenger');
    //     $this->history->obtainOld($idBookingTourPassenger);

    //     /** @var IndexBookingTourPassengerResponse */
    //     $indexResponse = $this->queryBus->ask(new IndexBookingTourPassengerQuery($idBookingTourPassenger));

    //     $statusTo = $request->input('BookingTourPassenger_Status');

    //     if ($indexResponse->response()[0]->BookingTourPassenger_Status == $request->input('BookingTourPassenger_Status')) {
    //         return $this->buildRequestError('El estado del pasajero es el mismo al que quiere cambiar');
    //     }

    //     if (!$this->canChangeTo($indexResponse->response()[0], $request->input('BookingTourPassenger_Status'))) {
    //         return $this->buildRequestError('El estado al que se quiere cambiar no está permitido');
    //     }

    //     if ($statusTo === BookingTourPassengerStatus::CANCELLED) {
    //         /** @var CancelBookingTourPassengerResponse $response */
    //         $response = $this->queryBus->ask(new CancelBookingTourPassengerQuery($idBookingTourPassenger));
    //     } else if ($indexResponse->response()['Response_Data']->BookingTourPassenger_Status === BookingTourPassengerStatus::CANCELLED) {
    //         /** @var ActivateBookingTourPassengerResponse $response */
    //         $response = $this->queryBus->ask(new ActivateBookingTourPassengerQuery($idBookingTourPassenger));
    //         $oParam = array(
    //             $request->input("Id_BookingTourPassenger"),
    //             $request->input("BookingTourPassenger_Status"),
    //             1
    //         );
    //         $oData = DB::select('call sp_booking_tour_passenger_status(?,?,?)', $oParam);
    //     } else {
    //         $oParam = array(
    //             $request->input("Id_BookingTourPassenger"),
    //             $request->input("BookingTourPassenger_Status"),
    //             1
    //         );
    //         $oData = DB::select('call sp_booking_tour_passenger_status(?,?,?)', $oParam);
    //     }

    //     $this->history->update(
    //         $this->findBooking($request->input('Id_BookingTourPassenger')),
    //         $this->history->oldValue,
    //         $this->history->old($request->input('Id_BookingTourPassenger')),
    //         $request
    //     );

    //     (new BookingTourPassengerNotificationCreator($this->commandBus))
    //         ->__invoke($idBookingTourPassenger, $request->header('Token'));

    //     $this->history->update(
    //         $this->findBooking($idBookingTourPassenger),
    //         $this->history->oldValue,
    //         $this->history->old($idBookingTourPassenger),
    //         $request
    //     );

    //     return ResponseBuilder::Response($response->response());
    // }

    public function Status(Request $request)
    {
        LaravelValidator::request($request, $this->oDomain, BookingTourPassengerValidators::STATUS_CHANGE);

        $oResponse  = array();
        $id = $request->input("Id_BookingTourPassenger");
        $status = $request->input("BookingTourPassenger_Status");
        $keep = $request->input("Keep_InvoiceDetail");

        $this->history->obtainOld($id);

        /** @var IndexBookingTourPassengerResponse */
        $index = $this->queryBus->ask(new IndexBookingTourPassengerQuery($id));
        $lastStatus = $index->response()[0]->BookingTourPassenger_Status;

        if ($status === 1 && $lastStatus !== $status) {
            LaravelValidator::request($request, $this->oDomain, BookingTourPassengerValidators::NULLIFY);
        }

        $oParam = [$id, $status, $keep];
        $response = Requester::requestUpdate('call sp_booking_tour_passenger_status_alter(?,?,?,@sts,@cod,@msg)', $oParam);

        $this->passengerUpdater->updateLockHtlCampBookingPassenger($index->response()[0]->Id_Passenger);

        (new BookingTourPassengerNotificationCreator($this->commandBus))
            ->__invoke($request->input("Id_BookingTourPassenger"), $request->header('Token'));

        try {
            $this->history->update(
                $this->findBooking($id),
                $this->history->oldValue,
                $this->history->old($id),
                $request
            );
        } catch (Throwable $th) {
        }
        return ResponseBuilder::Response($response);
    }

    public function Reportable(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id_BookingTourPassenger'           => 'required|int',
            'BookingTourPassenger_Reportable'   => 'required|int'
        );

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

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

            $oParam = array(
                $Request->input("Id_BookingTourPassenger"),
                $Request->input("BookingTourPassenger_Reportable"),
            );
            $oData  = DB::select('call sp_booking_tour_passenger_reportable(?,?)', $oParam);

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $this->history->update(
                    $this->findBooking($Request->input('Id_BookingTourPassenger')),
                    $this->history->oldValue,
                    $this->history->old($Request->input('Id_BookingTourPassenger')),
                    $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 Hotel(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id_BookingTourPassenger'       => 'required|int',
            'BookingTourPassenger_Hotel'    => 'string'
        );

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {

            $this->history->obtainOld($Request->input('Id_BookingTourPassenger'));

            $oParam = array(
                $Request->input("Id_BookingTourPassenger"),
                $Request->input("BookingTourPassenger_Hotel"),
            );
            $oData  = DB::select('call sp_booking_tour_passenger_hotel(?,?)', $oParam);

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $this->history->update(
                    $this->findBooking($Request->input('Id_BookingTourPassenger')),
                    $this->history->oldValue,
                    $this->history->old($Request->input('Id_BookingTourPassenger')),
                    $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 Remark(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id_BookingTourPassenger'       => 'required|int',
            'BookingTourPassenger_Remark'   => 'required|string'
        );

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

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

            $oParam = array(
                $Request->input("Id_BookingTourPassenger"),
                $Request->input("BookingTourPassenger_Remark"),
            );
            $oData  = DB::select('call sp_booking_tour_passenger_remark(?,?)', $oParam);

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $this->history->update(
                    $this->findBooking($Request->input('Id_BookingTourPassenger')),
                    $this->history->oldValue,
                    $this->history->old($Request->input('Id_BookingTourPassenger')),
                    $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);
    }

    private function findBooking($Id_BookingTourPassenger)
    {
        $found = DB::select(
            'Call sp_booking_tour_passenger_index(?, ?)',
            [$Id_BookingTourPassenger, config('var.PATH_PUBLIC') . config('var.USER_COUNTRY_THUMB')]
        );

        return count($found) ? $found[0]->Id_Booking : null;
    }

    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, config('var.PATH_PUBLIC') . config('var.USER_COUNTRY_THUMB'));
            $oData  = DB::select('call sp_booking_tour_passenger_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 List(Request $Request, $Id_BookingTour, $Action)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id' => 'required|int'
        );

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {

            $oResponse = ParamsValidator::Validate_Id($Id_BookingTour, $this->oDomain);

            if ($oResponse["Response_Code"] == 200) {

                $oResponse = ParamsValidator::Validate_Action($Action, $this->oDomain);

                $Path_Thumb = config("var.PATH_PUBLIC") . config("var.USER_COUNTRY_THUMB");
                if ($oResponse["Response_Code"] == 200) {
                    $oParam = array(
                        $Action,
                        $Id_BookingTour,
                        $Request->input("Id"),
                        $Path_Thumb
                    );

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

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

        return ResponseBuilder::Response($oResponse);
    }

    public function Search(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id_Tour'   => 'required|int',
            'Date'      => 'required|string'
        );

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $Path_Thumb     = config("var.PATH_PUBLIC") . config("var.USER_COUNTRY_THUMB");
            $oParam = [
                $Request->input("Id_Tour"),
                $Request->input("Date"),
                $Path_Thumb
            ];
            $oData  = DB::select('call sp_booking_tour_passenger_search(?,?,?)', $oParam);

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

        return ResponseBuilder::Response($oResponse);
    }

    public function Selector(Request $Request, $Id)
    {
        if (is_numeric($Id)) {
            return SELF::Index($Request, $Id);
        } else {
            return SELF::Search($Request);
        }
    }
}
