<?php

namespace App\Http\Controllers\Client;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Owner\Booking\BookingRoomCreator;
use App\Http\Controllers\Owner\BookingCreator;
use App\Http\Controllers\Owner\Passenger\PassengerUpdater;
use Illuminate\Http\Request;
use App\Http\Helpers\ResponseBuilder;
use App\Http\Helpers\ParamsValidator;
use App\Mail\c_Mail_User;
use App\Modules\BookingTemp\Infrastructure\BookingTempValidators;
use App\Modules\PackageFee\Application\GetFull\PackageFeeGetFullQuery;
use App\Modules\PackageFee\Application\GetFull\PackageFeeGetFullQueryResponse;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Domain\ValueObject\DateValueObject;
use App\Modules\Shared\Validator\Infraestructure\LaravelDTOGenerator;
use App\Providers\BookingMailSender;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Throwable;

class c_Booking_Temp extends Controller
{
    private $oDomain;

    public function __construct(
        private QueryBus $queryBus,
        private BookingCreator $bookingCreator,
        private PassengerUpdater $passengerUpdater,
        private LaravelDTOGenerator $dtoGenerator,
        private BookingRoomCreator $bookingRoomCreator
    ) {
        $this->oDomain = "Booking Temp";
    }

    public function Index($Id)
    {

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

        if ($oResponse["Response_Code"] == 200) {
            $oData  = DB::select('call sp_client_niubiz_purchase_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);
    }

    private function updateBookingTemp($id, $message, $responseData, $status)
    {
        return DB::select('CALL sp_client_niubiz_purchase_update(?,?,?,?)', [$id, $message, $responseData, $status]);
    }

    public function ValidateBooking(Request $Request, $Id)
    {
        $oResponse  = array();
        $oValParams     = BookingTempValidators::VALIDATE_BOOKING;

        $credentials = BookingMailSender::getCredentialList();
        $merchantId = BookingMailSender::filterCredential($credentials, 'Niubiz_MerchantId')[0];
        $credentialWebName = 'Internal_Web_URL';
        if ($Request->input('useWeb', '')) {
            $credentialWebName = 'Web_URL';
        }

        $webUrl = BookingMailSender::filterCredential($credentials, $credentialWebName)[0]->Credential_Value . 'checkout';

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

        if ($oResponse['Response_Code'] != 200) {
            $this->updateBookingTemp($Id, $oResponse['Response_Message'], '', 1);
            return redirect()->to($webUrl . '?bookingErrorId=' . $Id);
        }

        $oData = DB::select('CALL sp_client_niubiz_purchase_index(?)', [$Id]);

        if (!count($oData)) {
            $this->updateBookingTemp($Id, 'The booking temp doesn\'t exists', '', 1);
            return redirect()->to($webUrl . '?bookingErrorId=' . $Id);
        }

        if ($oData[0]->BookingTemp_Status == 2 && $oData[0]->BookingTemp_ResponseData != '') {
            return redirect()->to($webUrl . '?bookingErrorId=' . $Id);
        }

        $niubiz = new c_Niubiz();

        $res = $niubiz->getTransactionAuthorization(
            $oData[0]->BookingTemp_AccessToken,
            $Request->input('transactionToken'),
            $oData[0]->Id_NiubizPurchase,
            $oData[0]->BookingTemp_Amount,
            $merchantId->Credential_Value
        );

        if (!$res['success']) {
            if (is_string($res['body'])) {
                $this->updateBookingTemp($Id, 'Niubiz Transaction Error: ' . $res['body'], $res['body'], 1);
                return redirect()->to($webUrl . '?bookingErrorId=' . $Id);
            } else {
                $message = isset($res['body']->data->ACTION_DESCRIPTION) ? $res['body']->data->ACTION_DESCRIPTION : $res['body']->errorMessage;
                $this->updateBookingTemp($Id, $message, json_encode($res['body']), 1);
                return redirect()->to($webUrl . '?bookingErrorId=' . $Id);
            }
        }

        $data = $res['body'];
        $this->updateBookingTemp($Id, '', json_encode($data), 2);


        $request = new Request(
            [
                'Id_User'               => $oData[0]->BookingTemp_Id_User,
                'User_Name'             => $oData[0]->BookingTemp_User_Name,
                'User_LastName'         => $oData[0]->BookingTemp_User_LastName,
                'User_Email'            => $oData[0]->BookingTemp_User_Email,
                'User_Phone'            => $oData[0]->BookingTemp_User_Phone,
                'Id_UserCountry'        => $oData[0]->BookingTemp_Id_UserCountry,
                'Booking'               => $oData[0]->BookingTemp_Booking,
                'CashMovement'          => json_encode(
                    [
                        'CashMovement_Amount' => $data->order->amount,
                        'CashMovement_Currency' => $data->order->currency,
                        'CashMovement_Name' => '-',
                        'CashMovement_LastName' => '-',
                        'CashMovement_Email' => '-',
                        'CashMovement_Country' => '-',
                        'CashMovement_City' => '-',
                        'CashMovement_Address' => '-',
                        'CashMovement_Phone' => '-',
                        'CashMovement_ReceiptNumber' => $data->order->purchaseNumber,
                        'CashMovement_CardLast' => '-',
                        'CashMovement_CardNumber' => $data->dataMap->CARD,
                        'CashMovement_CardBrand' => $data->dataMap->BRAND,
                        'CashMovement_CardType' => '-',
                        'CashMovement_CardIssuer' => '-',
                    ]
                ),
            ]
        );
        $request->headers->set('Token', $oData[0]->BookingTemp_Token);

        return  $this->InsertBooking($request, BookingMailSender::filterCredential($credentials, 'Internal_Web_URL')[0]->Credential_Value, $webUrl, $Id, $data);
    }

    public function returnException($code, $message, $errorData = [])
    {
        $oResponse = [];
        $oResponse["Response_Status"]           = $code;
        $oResponse["Response_Code"]             = 406;
        $oResponse["Response_Domain"]           = $this->oDomain;
        $oResponse["Response_Message"]          = $message;
        $oResponse["Response_Data"]             = $errorData;
        $oResponse["Response_Error_Message"]    = $message;
        $oResponse["Response_Error_Reason"]     = $message;

        return ResponseBuilder::Response($oResponse);
    }

    public function InsertBooking(Request $Request, $webUrl, $cartUrl, $Id_BookingTemp, $data)
    {
        $cBooking = new c_Booking($this->queryBus, $this->bookingCreator, $this->passengerUpdater, $this->dtoGenerator, $this->bookingRoomCreator);

        $Id_User            = 0;
        $Id_Booking         = 0;
        $Id_BookingTour     = 0;
        $Id_BookingUpgrade  = 0;
        $Id_BookingExtra    = 0;
        $Token              = "";
        $oData              = null;
        $Response_Data = null;

        $oResponse  = array();
        $oValParams = array(
            'Id_User'               => 'int',
            'User_Name'             => 'required|string|max:100',
            'User_LastName'         => 'required|string|max:100',
            'User_Email'            => 'required|string|max:250',
            'User_Phone'            => 'string|max:20',
            'Id_UserCountry'        => 'required|int',
            'Booking'               => 'required|string',
            'CashMovement'          => 'required|string'
        );

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

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

            $userId = $Request->input('Id_User');
            $Token      = $Request->header("Token");
            if ($Request->input('Id_User') == 0) {
                $oClient = DB::select('CALL sp_client_user_validate(?)', [$Request->input('User_Email')]);
                if ($oClient[0]->Response_Id != null) {
                    $userId = $oClient[0]->Response_Id;
                    $Token = DB::select('CALL sp_client_user_session_insert_empty(?)', [$userId])[0]->Response_Token;
                }
            }

            if ($userId == 0) {
                $oParam     = [
                    $Request->input("User_Name"),
                    $Request->input("User_LastName"),
                    $Request->input("User_Email"),
                    $Request->input("User_Phone"),
                    $Request->input("Id_UserCountry")
                ];

                $oData_User = DB::select('call sp_client_user_insert(?,?,?,?,?)', $oParam);
                $Id_User = $oData_User[0]->Response_Id;

                if ($oData_User[0]->Response_Success == 1) {

                    $Variables  = [
                        "Lang"          => 1,
                        "User"          => $Request->input("User_Email"),
                        "Password"      => $oData_User[0]->Response_Password,
                        'ResourcesUrl'  => BookingMailSender::getResourcesUrl()[0]->Credential_Value,
                        'WebUrl'        => BookingMailSender::getClientWebUrl()[0]->Credential_Value
                    ];

                    $oMail_User             = new c_Mail_User();
                    $oMail_User->Type       = 1;
                    $oMail_User->Subject    = "Your Account Password";
                    $oMail_User->Content    = $Variables;

                    try {
                        if (config('var.SEND_AUTO_ACCESSDATA')) {
                            Mail::to($Request->input("User_Email"))->send($oMail_User);
                        } else {
                            Mail::to(config('var.SEND_EMAIL_ACCESSDATA'))->send($oMail_User);
                        }
                    } catch (Throwable $ex) {
                    }
                }

                $oParam = array(
                    $oData_User[0]->Response_User_Email,
                    $oData_User[0]->Response_Password
                );

                $oData_User_Session = DB::select('call sp_client_user_session_insert(?,SHA1(?))', $oParam);

                $Token              = $oData_User_Session[0]->Response_Token;
            } else {
                $Id_User    = $userId;
            }

            //
            //  DESERIALIZA DATOS DE LA RESERVA
            //
            $oBooking       = json_decode($Request->input("Booking"));

            $oCashMovement  = json_decode($Request->input("CashMovement"));

            $oParam = [
                DateValueObject::reducedNowToPeru(),
                $oCashMovement->CashMovement_Amount,
                $oCashMovement->CashMovement_Currency,
                $Request->input("User_Name"),
                $Request->input("User_LastName"),
                $oCashMovement->CashMovement_Email,
                $oCashMovement->CashMovement_Country,
                $oCashMovement->CashMovement_City,
                $oCashMovement->CashMovement_Address,
                $oCashMovement->CashMovement_Phone,
                $oCashMovement->CashMovement_ReceiptNumber,
                $oCashMovement->CashMovement_CardLast,
                $oCashMovement->CashMovement_CardNumber,
                $oCashMovement->CashMovement_CardBrand,
                $oCashMovement->CashMovement_CardType,
                $oCashMovement->CashMovement_CardIssuer,
                '',
                2
            ];

            $oDataCashMovement = DB::select('call sp_cash_movement_insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)', $oParam);

            if ($oDataCashMovement[0]->Response_Success) {
                $Id_CashMovement = $oDataCashMovement[0]->Id_CashMovement;
                if (count($oBooking) > 0) {
                    $oData = $oData_Booking = $cBooking->insertGenericBooking($oBooking[0], $Id_User);

                    if ($oData_Booking[0]->Response_Success == 1) {
                        $Id_Booking = $oData_Booking[0]->Response_Id;

                        /** @var object $oBookingHighPassenger */
                        $oBookingHighPassenger = $cBooking->getBookingHighPassenger($oBooking);

                        $insertedPassengers = $cBooking->insertPassengerList(
                            $oBookingHighPassenger->Passenger,
                            $Id_Booking,
                            2
                        );

                        $oPassenger = $cBooking->listPassengers($Id_Booking);

                        foreach ($oBooking as $k => $oBookingElement) {

                            $oTour          = $oBooking[$k]->Tour;

                            foreach ($oTour as $j => $oTourElement) {

                                try {
                                    /** @var PackageFeeGetFullQueryResponse $response */
                                    $response = $this->queryBus->ask(new PackageFeeGetFullQuery($oBooking[$k]->Id_Package));

                                    $oParam         = array(
                                        $oTour[$j]->BookingTour_DateStart,
                                        $oBooking[$k]->Booking_Type,
                                        $oBooking[$k]->Booking_NoPax,
                                        $response->packageFeeType(),
                                        $response->packageFeeTotal(),
                                        $oBooking[$k]->Id_Package,
                                        $Id_Booking,
                                        $oTour[$j]->Id_Tour,
                                        $k + 1
                                    );

                                    $oData_Tour     = DB::select('call sp_booking_tour_insert_unregistered(?,?,?,?,?,?,?,?,?)', $oParam);

                                    $Id_BookingTour = $oData_Tour[0]->Response_Id;
                                    $camps = $this->bookingCreator->addHtlCampBookings($Id_BookingTour, $oTour[$j]->BookingTour_DateStart, $oTour[$j]->Id_Tour);
                                    $oUpgrade       = $oTour[$j]->Upgrade;
                                    $oExtra         = $oTour[$j]->Extra;

                                    for ($h = 0; $h < count($oPassenger) && $h < $oBookingElement->Booking_NoPax; $h++) {

                                        $oParam                 = array(
                                            $Id_BookingTour,
                                            $oPassenger[$h]->Id_Passenger,
                                            count($oPassenger)
                                        );

                                        $oResponseTourPassenger = DB::select('call sp_booking_tour_passenger_insert(?,?,?)', $oParam);
                                        $idBookingTourPassenger = $oResponseTourPassenger[0]->Response_Id;
                                        /** @var array $camps */
                                        array_map(fn($camp) => $this->bookingCreator->addHtlCampBookingPassenger(
                                            $camp->id(),
                                            $idBookingTourPassenger
                                        ), $camps);
                                    }

                                    $oParam = array(
                                        "all",
                                        $Id_BookingTour,
                                        0,
                                        ''
                                    );

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

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

                                        $oParam             = array(
                                            $oBooking[$k]->Booking_NoPax,
                                            "",
                                            2,
                                            1,
                                            1,
                                            $Id_BookingTour,
                                            0,
                                            $oUpgrade[$i]->Id_Upgrade,
                                            1,
                                            1
                                        );

                                        $oData_Upgrade      = DB::select('call sp_booking_upgrade_insert(?,?,?,?,?,?,?,?,?,?)', $oParam);
                                        $Id_BookingUpgrade  = $oData_Upgrade[0]->Response_Id;

                                        for ($h = 0; $h < count($oTourPassenger); $h++) {


                                            $oParam                     = array(
                                                $Id_BookingUpgrade,
                                                $oTourPassenger[$h]->Id_BookingTourPassenger,
                                                1
                                            );
                                            $oData_UpgradePassenger     = DB::select('call sp_booking_upgrade_passenger_insert(?,?,?)', $oParam);
                                        }
                                    }

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

                                        $oParam             = array(
                                            $oBooking[$k]->Booking_NoPax,
                                            " ",
                                            2,
                                            1,
                                            1,
                                            $Id_BookingTour,
                                            0,
                                            $oExtra[$i]->Id_Extra,
                                            1,
                                            1
                                        );

                                        $oData_Extra        = DB::select('call sp_booking_extra_insert(?,?,?,?,?,?,?,?,?,?)', $oParam);
                                        $Id_BookingExtra    = $oData_Extra[0]->Response_Id;

                                        for ($h = 0; $h < count($oTourPassenger); $h++) {

                                            $oParam                 = array(
                                                $Id_BookingExtra,
                                                $oTourPassenger[$h]->Id_BookingTourPassenger,
                                                1
                                            );

                                            $oData_ExtraPassenger   = DB::select('call sp_booking_extra_passenger_insert(?,?,?)', $oParam);
                                        }
                                    }

                                    DB::select('CALL sp_booking_tour_add_items_from_package_tour(?,?)', [$Id_BookingTour, 1]);
                                } catch (Throwable $th) {
                                }
                            }
                        }

                        $passengerLength = count($oPassenger);

                        for ($i = 0; $i < $passengerLength; $i++) {
                            $oParam = [
                                $Id_Booking,
                                $oPassenger[$i]->Id_Passenger,
                                1
                            ];

                            $oData_Invoice  = DB::select('call sp_invoice_insert(?,?,?)', $oParam);

                            $oData          = $oData_Invoice;
                            $sum = 0;
                            foreach ($oBooking as $j => $booking) {
                                if ($i >= $booking->Booking_NoPax) {
                                    continue;
                                }
                                $sum += $booking->CashMovementDetail_Amount / $booking->Booking_NoPax;
                            }

                            DB::select(
                                'call sp_cash_movement_detail_insert(?,?,?,?,?)',
                                [
                                    $sum,
                                    $oDataCashMovement[0]->Id_CashMovement,
                                    $oData_Invoice[0]->Response_Id,
                                    1,
                                    1
                                ]
                            );
                            $this->passengerUpdater->updateLockHtlCampBookingPassenger($oPassenger[$i]->Id_Passenger);
                        }

                        if (isset($oBooking[0]->Rooms)) {
                            $this->bookingRoomCreator->createRooms($oBooking[0]->Rooms, $Id_Booking, $insertedPassengers);
                        }

                        try {
                            BookingMailSender::sendBookingByMail($Id_Booking, $this->oDomain, BookingMailSender::REGULAR_AND_PAYMENT);
                        } catch (\Throwable $ex) {
                        }
                    }
                }
            } else {
                $oData = $oDataCashMovement;
            }

            $Response_Data  = array(
                "Id_Booking"    => $Id_Booking,
                "Id_User"       => $Id_User,
                "Token"         => $Token
            );


            $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"]             = $Response_Data;
            $oResponse["Response_Error_Message"]    = $oData[0]->Response_Message;
            $oResponse["Response_Error_Reason"]     = $oData[0]->Response_Reason;
        }
        if ($oResponse["Response_Status"] == 200) {
            return redirect()->to($webUrl . '?clean=1&data=' . json_encode($Response_Data) . '&cashId=' . $Id_CashMovement);
        } else {
            $this->updateBookingTemp($Id_BookingTemp, $oResponse["Response_Error_Reason"], json_encode($data), 2);
            return redirect()->to($cartUrl . '?bookingErrorId=' . $Id_BookingTemp);
        }

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