<?php

namespace App\Http\Controllers\Owner\Booking;

use App\Http\Controllers\Owner\BookingCreator;
use App\Http\Controllers\Owner\Passenger\PassengerUpdater;
use App\Http\Helpers\ResponseBuilder;
use App\Http\History\HistoryData;
use App\Http\History\HistoryTables;
use App\Mail\c_Mail_User;
use App\Modules\Booking\Infraestructure\BookingHistory;
use App\Modules\Booking\Infraestructure\BookingValidators;
use App\Modules\BookingAssign\Application\Create\CreateBookingAssignQuery;
use App\Modules\BookingAssign\Domain\BookingAssignStatus;
use App\Modules\PackageFee\Application\GetFull\PackageFeeGetFullQuery;
use App\Modules\Shared\Application\InsertHandler;
use App\Modules\Shared\Application\SelectHandler;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Validator\Infraestructure\LaravelValidator;
use App\Modules\TypeBookingAssign\Domain\IdTypeBookingAssign;
use App\Modules\WebContactMessage\Application\UpdateIdBooking\UpdateIdBookingWebContactMessageQuery;
use App\Providers\BookingMailSender;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Throwable;

class BookingAlterInsertController
{
    private HistoryData $history;
    private HistoryData $historyBookingTour;

    private $domain = 'Booking';

    public function __construct(
        private QueryBus $queryBus,
        private BookingCreator $bookingCreator,
        private PassengerUpdater $passengerUpdater,
        private BookingUpdater $bookingUpdater,
        private LaravelValidator $validator
    ) {
        $this->history = new HistoryData(HistoryTables::BOOKING);
        $this->historyBookingTour = new HistoryData(HistoryTables::BOOKING_TOUR);
    }

    private function objectToArray($data)
    {
        $result = [];
        foreach ($data as $key => $value) {
            $result[$key] = (is_array($value) || is_object($value)) ? $this->objectToArray($value) : $value;
        }
        return $result;
    }

    public function __invoke(Request $request)
    {
        (new LaravelValidator())->validate($request->all(), $this->domain, BookingValidators::ALTER_INSERT);
        $oBooking = json_decode($request->input("Booking"));
        if ($oBooking == null) {
            throw new BadRequestHttpException('Booking is required');
        }
        if (!count($oBooking)) {
            throw new BadRequestHttpException('Booking is required');
        }

        foreach ($oBooking as $booking) {
            (new LaravelValidator())->validate($this->objectToArray($booking), $this->domain, BookingValidators::ALTER_INSERT_BOOKING);
        }

        if ($request->input("Id_User") == 0) {
            $Id_User = $this->findOrCreateUser(
                $request->input("User_Name"),
                $request->input("User_LastName"),
                $request->input("User_Email"),
                $request->input("User_Phone"),
                $request->input("Id_UserCountry")
            );
        } else {
            $Id_User = $request->input("Id_User");
        }

        $webContactMessage = $request->input("WebContactMessage");

        $admin = (new SelectHandler())->handle(DB::select('CALL sp_admin_index_token(?)', [$request->header('Token')]))[0];

        $oData = $this->createBooking($oBooking[0], $Id_User, $request->header('Token'));
        $Id_Booking = $oData[0]->Response_Id;
        $booking = (new SelectHandler())->handle(DB::select('CALL sp_booking_index(?)', [$Id_Booking]))[0];
        $responseData[] = [
            'Id_Booking' => $Id_Booking,
            'Booking_Code' => $booking->Booking_Code
        ];
        /** @var object $oBookingHighPassenger */
        $oBookingHighPassenger = $this->getBookingHighPassenger($oBooking);

        $this->insertPassengerList($oBookingHighPassenger->Passenger, $oBookingHighPassenger->Booking_NoPax, $Id_Booking);

        $oPassenger = $this->getPassengerList($Id_Booking);

        for ($k = 0; $k < count($oBooking); $k++) {

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

            for ($j = 0; $j < count($oTour); $j++) {
                /** @var PackageFeeGetFullQueryResponse $response */
                $response = $this->queryBus->ask(new PackageFeeGetFullQuery($oBooking[$k]->Id_Package));

                $oData_Tour = $this->insertBookingTour($oBooking[$k], $oTour[$j], $Id_Booking, $response, $k + 1);
                $Id_BookingTour = $oData_Tour[0]->Response_Id;
                $camps = $this->bookingCreator->addHtlCampBookings($Id_BookingTour, $oTour[$j]->BookingTour_DateStart, $oTour[$j]->Id_Tour);

                for ($h = 0; $h < count($oPassenger) && $h < $oBooking[$k]->Booking_NoPax; $h++) {
                    $oParam = [$Id_BookingTour, $oPassenger[$h]->Id_Passenger, $oBooking[$k]->Booking_NoPax];
                    $oResponseTourPassenger = (new InsertHandler())->handle(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);
                }

                (new InsertHandler())->handle(DB::select('CALL sp_booking_tour_add_items_from_package_tour(?,?)', [$Id_BookingTour, 1]));
            }
        }

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

            $oData_Invoice  = (new InsertHandler())->handle(DB::select('call sp_invoice_insert(?,?,?)', $oParam));
            $oData = $oData_Invoice;
        }
        /** @var CreateWebContactResponse $createWebContactResponse */
        /** @var CreateBookingAssignResponse $response */
        $this->queryBus->ask(new CreateBookingAssignQuery(
            '',
            BookingAssignStatus::defaultValue(),
            $Id_Booking,
            $admin->Id_Admin,
            IdTypeBookingAssign::defaultValue()
        ));
        if ($webContactMessage != null) {
            foreach ($webContactMessage as $wcm) {
                $this->queryBus->ask(new UpdateIdBookingWebContactMessageQuery($wcm, $Id_Booking));
            }
        }
        if ($request->input("Id_WebContactMessage") != null) {
            try {
                $this->queryBus->ask(new UpdateIdBookingWebContactMessageQuery($request->input("Id_WebContactMessage"), $Id_Booking));
            } catch (Exception $e) {
            }
        }
        $this->history->insert(
            BookingHistory::getHistoryData($Id_Booking),
            $request,
            $Id_Booking,
            false
        );

        $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"]             = $responseData;
        $oResponse["Response_Error_Message"]    = $oData[0]->Response_Message;
        $oResponse["Response_Error_Reason"]     = $oData[0]->Response_Reason;


        return ResponseBuilder::Response($oResponse);
    }

    private function insertBookingTour(
        $booking,
        $bookingTour,
        $idBooking,
        $response,
        $group
    ) {
        $packageRate = DB::select('SELECT `Id_PackageRate`, `PackageRate_Type` FROM `t_package_rate`
            WHERE `Id_Package` = ?
            AND `PackageRate_Status` = 2', [$booking->Id_Package]);
        if (count($packageRate) == 0) {
            $packageRateType = 1;
        } else {
            $packageRateType = $packageRate[0]->PackageRate_Type;
        }
        $oParam = [
            $bookingTour->BookingTour_DateStart,
            $packageRateType, //$booking->Booking_Type,
            $booking->Booking_NoPax,
            $response->packageFeeType(),
            $response->packageFeeTotal(),
            $booking->Id_Package,
            $idBooking,
            $bookingTour->Id_Tour,
            $group
        ];
        return (new InsertHandler())->handle(DB::select('call sp_booking_tour_insert_unregistered(?,?,?,?,?,?,?,?,?)', $oParam));
    }

    private function insertPassengerList($passengerList, $noPax, $Id_Booking)
    {
        for ($j = 0; $j < $noPax; $j++) {
            $index = count($passengerList) > $j ? $j : 0;
            $default_date = "1000-01-01 00:00:00";
            $SkipRestrictions = 2;
            $oParam = array(
                Str::title($passengerList[$index]->Passenger_Name),
                Str::title($passengerList[$index]->Passenger_LastName),
                $passengerList[$index]->Passenger_Email,
                '',
                $passengerList[$index]->Passenger_Phone,
                $passengerList[$index]->Passenger_Gender,
                $passengerList[$index]->Passenger_DOB,
                Str::upper(trim($passengerList[$index]->Passenger_NoDocument)),
                $default_date,
                $passengerList[$index]->Passenger_Restriction,
                $passengerList[$index]->Passenger_Extras,
                2,
                2,
                $Id_Booking,
                $passengerList[$index]->Id_TypeDocument,
                $passengerList[$index]->Id_TypeRate,
                $passengerList[$index]->Id_UserCountry,
                $SkipRestrictions
            );

            (new InsertHandler())->handle(DB::select('call sp_passenger_insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', $oParam));
        }
    }

    public function getBookingHighPassenger(array $bookingList)
    {
        return array_reduce($bookingList, function ($carry, $booking) {
            if ($carry == null) {
                return $booking;
            }
            /** @var object $carry */
            return $carry->Booking_NoPax < $booking->Booking_NoPax ? $booking : $carry;
        }, null);
    }

    public function getPassengerList($Id_Booking)
    {
        $oParam = ['all', $Id_Booking, 0, '', '', '', ''];
        return (new SelectHandler())->handle(DB::select('call sp_passenger_list(?,?,?,?,?,?,?)', $oParam));
    }

    private function createBooking(
        $booking,
        $Id_User,
        $token
    ) {
        return (new InsertHandler())->handle(DB::select(
            'call sp_booking_insert(?,?,?,?,?,?,?,?,?,?,?,?,?)',
            [
                $booking->Booking_DateStart,
                $booking->Booking_Receipt,
                $booking->Booking_Type_Real,
                '',
                '',
                $booking->Id_Language,
                $booking->Id_GuideLanguage,
                $Id_User,
                0,
                $booking->Booking_InPerson,
                $booking->Id_BookingRelationship,
                $booking->Id_BookingReferredFrom,
                $token
            ]
        ));
    }

    private function findOrCreateUser($User_Name, $User_LastName, $User_Email, $User_Phone, $Id_UserCountry)
    {
        $response = DB::select('CALL sp_client_user_validate(?)', [$User_Email]);
        if ($response[0]->Response_Id !== null) {
            return $response[0]->Response_Id;
        }
        $oParam = [
            $User_Name,
            $User_LastName,
            $User_Email,
            $User_Phone,
            $Id_UserCountry
        ];

        $response = (new InsertHandler())->handle(DB::select('call sp_client_user_insert(?,?,?,?,?)', $oParam));
        $this->sendMail($response[0]->Response_Password, $User_Email);
        return $response[0]->Response_Id;
    }

    public function sendMail($userPassword, $userEmail)
    {
        $Variables = [
            "Lang" => 1,
            "User" => $userEmail,
            "Password" => $userPassword,
            'ResourcesUrl' => BookingMailSender::getResourcesUrl()[0]->Credential_Value,
            'WebUrl' => BookingMailSender::getWebUrl()[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($userEmail)->send($oMail_User);
            } else {
                Mail::to(config('var.SEND_EMAIL_ACCESSDATA'))->send($oMail_User);
            }
        } catch (Throwable $ex) {
        }
    }

    // public function findOrCreateUser($Id_User, $User_Name, $User_LastName, $User_Email, $User_Phone, $Id_UserCountry)
    // {
    //     /** @var FindOrCreateUserResponse $response */
    //     $response = $this->queryBus->ask(new FindOrCreateUserQuery(
    //         $Id_User,
    //         $User_Name,
    //         $User_LastName,
    //         $User_Email,
    //         $User_Phone,
    //         $Id_UserCountry
    //     ));

    //     return $response->id();
    // }
}
