<?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\InvoiceDetail\Application\Index\IndexInvoiceDetailQuery;
use App\Modules\InvoiceDetail\Application\InvoiceDetailResponse;
use App\Modules\InvoiceDetail\Domain\InvoiceDetailMode;
use App\Modules\InvoiceDetail\Domain\InvoiceDetailType;
use App\Modules\Shared\Application\SelectHandler;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\TypeExtra\Domain\IdTypeExtra;
use App\Providers\StorageProvider;
use Illuminate\Support\Facades\DB;

class c_Invoice_Detail extends Controller
{
    private $oDomain;
    static $staticDomain = "Invoice Detail";
    /** @var HistoryData */
    private $history;
    private $start = 0;

    public function __construct(
        private QueryBus $queryBus,
        private PassengerUpdater $passengerUpdater
    ) {
        $this->oDomain = "Invoice Detail";
        $this->history = new HistoryData(HistoryTables::INVOICE_DETAIL);
    }

    public static function findBooking($Id_InvoiceDetail)
    {
        $found = DB::select('CALL `sp_invoice_detail_index`(?)', [$Id_InvoiceDetail]);
        return count($found) ? $found[0]->Id_Booking : null;
    }

    public function Insert(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'InvoiceDetail_Quantity'        => 'required|int',
            'InvoiceDetail_Type'            => 'required|int',
            'InvoiceDetail_Description'     => 'required|string',
            'InvoiceDetail_Price'           => 'required|numeric',
            'InvoiceDetail_DateEntryStart'  => 'required|date',
            'InvoiceDetail_DateEntryEnd'    => 'required|date',
            'Id'                            => 'required|int',
            'Id_Invoice'                    => 'required|int',
            'Id_Passenger'                  => 'required|int'
        );
        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request($Request, $oValParams, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            if ($Request->input("InvoiceDetail_Price") > 0) {
                $mode = InvoiceDetailMode::EXTRA;
            } else {
                $mode = InvoiceDetailMode::DISCOUNT;
            }
            $oParam = array(
                $Request->input("InvoiceDetail_Quantity"),
                $Request->input("InvoiceDetail_Description"),
                $Request->input("InvoiceDetail_Price"),
                0,
                1,
                $mode,
                $Request->input("InvoiceDetail_Type"),
                $Request->input("InvoiceDetail_Group") ?? 100,
                $Request->input('InvoiceDetail_DateEntryStart'),
                $Request->input('InvoiceDetail_DateEntryEnd'),
                $Request->input('InvoiceDetail_DateReturnStart'),
                $Request->input('InvoiceDetail_DateReturnEnd'),
                1,
                $Request->input("Id"),
                $Request->input("Id_Invoice"),
                0,
                1
            );
            $oData = DB::select('call sp_invoice_detail_insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', $oParam);

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $this->history->insert(
                    [$Request->all()],
                    $Request,
                    c_Invoice::findBooking($Request->input('Id_Invoice'))
                );

                $oParam         = array($Request->input("Id_Passenger"), 1);
                $oDataInvoice   = DB::select('call sp_invoice_reload(?,?)', $oParam);
                $this->passengerUpdater->updateLockHtlCampBookingPassenger($Request->input("Id_Passenger"));

                if ($oDataInvoice[0]->Response_Success == 0) {
                    $oData = $oDataInvoice;
                }
            } 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 InsertAll(Request $Request)
    {
        $oSucces    = true;
        $oResponse  = array();
        $oValParams = array('InvoiceDetails'   => 'required|string');


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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oDetails = json_decode($Request->input("InvoiceDetails"));

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

                if ($oDetails[$k]->InvoiceDetail_Price > 0) {
                    $mode = InvoiceDetailMode::EXTRA;
                } else {
                    $mode = InvoiceDetailMode::DISCOUNT;
                }
                $oParam = array(
                    $oDetails[$k]->InvoiceDetail_Quantity,
                    $oDetails[$k]->InvoiceDetail_Description,
                    $oDetails[$k]->InvoiceDetail_Price,
                    0,
                    1,
                    $mode,
                    $oDetails[$k]->InvoiceDetail_Type,
                    $oDetails[$k]->InvoiceDetail_Group ?? 100,
                    $oDetails[$k]->InvoiceDetail_DateEntryStart,
                    $oDetails[$k]->InvoiceDetail_DateEntryEnd,
                    $oDetails[$k]->InvoiceDetail_DateReturnStart,
                    $oDetails[$k]->InvoiceDetail_DateReturnEnd,
                    1,
                    $oDetails[$k]->Id,
                    $oDetails[$k]->Id_Invoice,
                    0,
                    1
                );
                $oData = DB::select('call sp_invoice_detail_insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', $oParam);

                //  RESPONSE
                if ($oData[0]->Response_Success == 1) {
                    $this->history->insert(
                        [
                            'InvoiceDetail_Quantity' => $oDetails[$k]->InvoiceDetail_Quantity,
                            'InvoiceDetail_Description' => $oDetails[$k]->InvoiceDetail_Description,
                            'InvoiceDetail_Price' => $oDetails[$k]->InvoiceDetail_Price,
                            'InvoiceDetail_Included' => 1,
                            'InvoiceDetail_Type' => $oDetails[$k]->InvoiceDetail_Type,
                            'InvoiceDetail_Group' => 100,
                            'Id' => $oDetails[$k]->Id,
                            'Id_Invoice' => $oDetails[$k]->Id_Invoice
                        ],
                        $Request,
                        c_Invoice::findBooking($oDetails[$k]->Id_Invoice)
                    );
                    $oParam         = array($oDetails[$k]->Id_Passenger, 1);

                    $oDataInvoice   = DB::select('call sp_invoice_reload(?,?)', $oParam);
                    $this->passengerUpdater->updateLockHtlCampBookingPassenger($oDetails[$k]->Id_Passenger);

                    if ($oData[0]->Response_Success == 0) {
                        $oSucces = false;
                        break;
                    }
                }
            }
            if ($oSucces == true) {
                $oData = array(
                    array(
                        "Response_Status"   => 200,
                        "Response_Code"     => 200,
                        "Response_Message"  => "Datos actualizados 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);
    }

    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);
            $idBooking = self::findBooking($Id);

            $oData = DB::select('call sp_invoice_detail_delete_admin(?, 1)', [$Id]);

            $this->history->delete(
                $idBooking,
                $this->history->oldValue,
                $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 startTime()
    {
        $this->start = microtime(true);
    }

    private function printTime($prepend = '')
    {
        $fin = microtime(true);
        echo $prepend . ($fin - $this->start) * 1000 . "\n";
        $this->start = $fin;
    }

    public function Update(Request $Request)
    {
        $this->startTime();
        $oResponse  = array();
        $oValParams = array(
            'Id_InvoiceDetail'              => 'required|int',
            'InvoiceDetail_Price'           => 'required|numeric',
            'InvoiceDetail_Description'     => 'max:250',
            'InvoiceDetail_DateEntryStart'  => 'date',
            'InvoiceDetail_DateEntryEnd'    => 'date',
            'InvoiceDetail_Group'           => 'int'
        );

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

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

            /** @var InvoiceDetailResponse */
            $indexResponse = $this->queryBus->ask(new IndexInvoiceDetailQuery($Request->input("Id_InvoiceDetail")));

            $indexInvoice = DB::select('CALL sp_invoice_get(?)', [$indexResponse->Id_Invoice]);

            $oParam = array(
                $Request->input("Id_InvoiceDetail"),
                $Request->input("InvoiceDetail_Price"),
                $Request->input('InvoiceDetail_Description') ?? $indexResponse->InvoiceDetail_Description,
                $Request->input('InvoiceDetail_DateEntryStart') ?? $indexResponse->InvoiceDetail_DateEntryStart,
                $Request->input('InvoiceDetail_DateEntryEnd') ?? $indexResponse->InvoiceDetail_DateEntryEnd,
                $Request->input('InvoiceDetail_Group') ?? $indexResponse->InvoiceDetail_Group
            );

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

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                DB::select('CALL sp_passenger_check_info_pay(?)', [$indexInvoice[0]->Id_Passenger]);
                DB::select('CALL sp_passenger_booking_count(?)', [$indexInvoice[0]->Id_Passenger]);
                $this->passengerUpdater->updateLockHtlCampBookingPassenger($indexInvoice[0]->Id_Passenger);
                $this->history->update(
                    self::findBooking($Request->input("Id_InvoiceDetail")),
                    $this->history->oldValue,
                    [$Request->all()],
                    $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 List(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_invoice_detail_list(?)', $oParam);

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $oResponse["Response_Message"]          = $this->oDomain . " list " . $Id;
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }
        return ResponseBuilder::Response($oResponse);
    }

    public static function ListAll(Request $Request)
    {
        $oSucces    = true;
        $oResponse  = array();
        $oValParams = array('Invoices' => 'required|string');

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request($Request, $oValParams, self::$staticDomain);

        //  INGRESANDO DATOS

        // -- 1 :: BOOKING UPGRADE - 2 :: BOOKING EXTRA - 3 :: BOOKING EQUIPMENT - 0 :: ID PAQUETE - 4 :: DETALLES MANUALES - 5 :: CANCELADOS - 9 :: IGV
        if ($oResponse["Response_Code"] == 200) {
            $oData = SELF::GetAll($Request->input('Invoices'));
            if (count($oData['Invoices'])) {
                $oData['BookingTour'] = (new SelectHandler())->handle(DB::select('call sp_booking_tour_list(?)', [$oData['Invoices'][0]->Id_Booking]));
            } else {
                $oData['BookingTour'] = null;
            }

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = self::$staticDomain;
            $oResponse["Response_Message"]          = self::$staticDomain . " list All ";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }

        return ResponseBuilder::Response($oResponse);
    }

    public static function detailIsManual($invoiceDetail)
    {
        return $invoiceDetail->InvoiceDetail_Type == InvoiceDetailType::Manual || $invoiceDetail->InvoiceDetail_Type == InvoiceDetailType::Canceled;
    }

    public static function isSameGroup($invoiceDetail1, $invoiceDetail2)
    {
        return $invoiceDetail1->InvoiceDetail_Group == $invoiceDetail2->InvoiceDetail_Group;
    }

    public static function isSameInstanceAndStatus($invoiceDetail1, $invoiceDetail2)
    {
        return $invoiceDetail1->Id == $invoiceDetail2->Id
            && $invoiceDetail1->InvoiceDetail_Type == $invoiceDetail2->InvoiceDetail_Type
            && $invoiceDetail1->InvoiceDetail_Status == $invoiceDetail2->InvoiceDetail_Status
            && $invoiceDetail1->InvoiceDetail_DateEntryStart == $invoiceDetail2->InvoiceDetail_DateEntryStart
            && $invoiceDetail1->InvoiceDetail_DateEntryEnd == $invoiceDetail2->InvoiceDetail_DateEntryEnd
            && $invoiceDetail1->InvoiceDetail_DateReturnStart == $invoiceDetail2->InvoiceDetail_DateReturnStart
            && $invoiceDetail1->InvoiceDetail_DateReturnEnd == $invoiceDetail2->InvoiceDetail_DateReturnEnd;
    }

    public static function isSameDescription($invoiceDetail1, $invoiceDetail2)
    {
        return $invoiceDetail1->InvoiceDetail_Type == $invoiceDetail2->InvoiceDetail_Type
            && $invoiceDetail1->InvoiceDetail_Description == $invoiceDetail2->InvoiceDetail_Description
            && $invoiceDetail1->InvoiceDetail_Price == $invoiceDetail2->InvoiceDetail_Price
            && $invoiceDetail1->InvoiceDetail_Mode == $invoiceDetail2->InvoiceDetail_Mode
            && $invoiceDetail1->InvoiceDetail_DateEntryStart == $invoiceDetail2->InvoiceDetail_DateEntryStart
            && $invoiceDetail1->InvoiceDetail_DateEntryEnd == $invoiceDetail2->InvoiceDetail_DateEntryEnd
            && $invoiceDetail1->InvoiceDetail_DateReturnStart == $invoiceDetail2->InvoiceDetail_DateReturnStart
            && $invoiceDetail1->InvoiceDetail_DateReturnEnd == $invoiceDetail2->InvoiceDetail_DateReturnEnd;
    }

    public static function cloneData($data)
    {
        return array_map(
            fn($item) => clone $item,
            $data
        );
    }

    public static function GetAll(string $invoiceListJSON)
    {
        /** @var StorageProvider $storageProvider */
        $storageProvider = app()->make(StorageProvider::class);
        $oInvoices  = json_decode($invoiceListJSON);
        $Invoices   = [];

        $Invoices_Detail    = [];
        $oData              = [];
        $val                = -1;
        $indice             = -1;
        $isigv              = 0;

        foreach ($oInvoices as $k => $inv) {
            $oParam = array($inv->Id);
            $invoices = DB::select('call sp_invoice_detail_list_alter(?)', $oParam);
            $invoices = array_map(function ($ele) use ($storageProvider) {
                $storageProvider->appendPublicToObject($ele, 'TypeUpgrade_Icon_Thumb');
                $storageProvider->appendPublicToObject($ele, 'TypeExtra_Icon_Thumb');
                $storageProvider->appendPublicToObject($ele, 'TypeEquipment_Icon_Thumb');
                return $ele;
            }, $invoices);
            $Invoices_Detail[] = self::cloneData($invoices);

            if (count($Invoices) == 0) {
                $Invoices = $invoices;
                continue;
            }

            foreach ($invoices as $i => $detail) {
                foreach ($Invoices as $j => $InvoicesElement) {
                    if ($InvoicesElement->InvoiceDetail_Type == 9) {
                        $isigv = 1;
                    }

                    if (self::isSameGroup($InvoicesElement, $detail)) {
                        $indice = $j;
                    }

                    if (self::isSameGroup($InvoicesElement, $detail)) {
                        if ($InvoicesElement->InvoiceDetail_Type != InvoiceDetailType::BookingTourPassenger) {
                            if ((!self::detailIsManual($InvoicesElement)
                                    && self::isSameInstanceAndStatus($InvoicesElement, $detail))
                                || (self::detailIsManual($InvoicesElement)
                                    && self::isSameDescription($InvoicesElement, $detail))
                            ) {
                                $val = $j;
                                break;
                            }
                        } else {
                            if (self::isSameDescription($InvoicesElement, $detail)) {
                                $val = $j;
                                break;
                            }
                        }
                    }
                }

                if ($val == -1) {
                    if ($indice == -1) {
                        if ($isigv == 0) {
                            array_push($Invoices, $detail);
                        } else {
                            array_splice($Invoices, count($Invoices) - 1, 0, array($detail));
                        }
                    } else {
                        array_splice($Invoices, $indice + 1, 0, array($detail));
                    }
                } else {
                    $InvoicesElement->InvoiceDetail_Total += $detail->InvoiceDetail_Total;
                    if ($InvoicesElement->InvoiceDetail_Type != InvoiceDetailType::Igv) {
                        $InvoicesElement->InvoiceDetail_Quantity += $detail->InvoiceDetail_Quantity;
                    }
                }
                $val = -1;
                $indice = -1;
            }
        }

        for ($k = 0; $k < count($Invoices); $k++) {
            for ($i = 0; $i < count($Invoices); $i++) {
                if (
                    $Invoices[$k]->Id == $Invoices[$i]->Id
                    && $Invoices[$k]->Id_InvoiceDetail != $Invoices[$i]->Id_InvoiceDetail
                    && self::isSameDescription($Invoices[$k], $Invoices[$i])
                ) {
                    $Invoices[$k]->InvoiceDetail_Quantity += $Invoices[$i]->InvoiceDetail_Quantity;
                    $Invoices[$k]->InvoiceDetail_Total += $Invoices[$i]->InvoiceDetail_Total;
                    array_splice($Invoices, $i, 1);
                }
            }
        }

        $oData = [];
        $oData["Invoices"] = $Invoices;
        $oData["Detail"] = $Invoices_Detail;

        return $oData;
    }

    private static function filterHotelIncluded($invoiceDetailList)
    {
        return array_values(array_filter($invoiceDetailList, fn($detail) => !self::isHotelIncluded($detail)));
    }

    private static function isHotelIncluded($detail)
    {
        $items = [InvoiceDetailType::Booking_Extra, InvoiceDetailType::Booking_Upgrade, InvoiceDetailType::Booking_Equipment];
        if (!in_array($detail->InvoiceDetail_Type, $items)) {
            return false;
        }
        if ($detail->InvoiceDetail_Type == InvoiceDetailType::Booking_Equipment) {
            $bookingEquipment = self::getBookingEquipment($detail->Id);
            if (!$bookingEquipment) {
                return false;
            }
            if ($bookingEquipment->BookingEquipment_Show == 1) {
                return true;
            }
            return false;
        }
        if ($detail->InvoiceDetail_Type == InvoiceDetailType::Booking_Upgrade) {
            $bookingUpgrade = self::getBookingUpgrade($detail->Id);
            if (!$bookingUpgrade) {
                return false;
            }
            if ($bookingUpgrade->BookingUpgrade_Show == 1) {
                return true;
            }
            return false;
        }

        $bookingExtra = self::getBookingExtra($detail->Id);
        if (!$bookingExtra) {
            return false;
        }
        if ($bookingExtra->BookingExtra_Show == 1) {
            return true;
        }
        if ($bookingExtra->Id_TypeExtra === IdTypeExtra::HOTEL && $bookingExtra->BookingExtra_Included == 2) {
            return true;
        }
    }

    private static function getBookingExtra($idBookingExtra)
    {
        $result = DB::select(
            'SELECT `be`.`BookingExtra_Included`, `be`.`BookingExtra_Show`, `be`.`Id_Extra`, `e`.`Id_TypeExtra`
            FROM `t_booking_extra` `be`
            INNER JOIN `t_extra` `e` ON `e`.`Id_Extra` = `be`.`Id_Extra`
            WHERE `be`.`Id_BookingExtra` = ?',
            [$idBookingExtra]
        );
        return count($result) ? $result[0] : null;
    }

    private static function getBookingUpgrade($idBookingUpgrade)
    {
        $result = DB::select(
            'SELECT `bu`.`BookingUpgrade_Show`, `bu`.`Id_Upgrade`
            FROM `t_booking_upgrade` `bu`
            WHERE `bu`.`Id_BookingUpgrade` = ?',
            [$idBookingUpgrade]
        );
        return count($result) ? $result[0] : null;
    }

    private static function getBookingEquipment($idBookingEquipment)
    {
        $result = DB::select(
            'SELECT `be`.`BookingEquipment_Show`, `be`.`Id_Equipment`
            FROM `t_booking_equipment` `be`
            WHERE `be`.`Id_BookingEquipment` = ?',
            [$idBookingEquipment]
        );
        return count($result) ? $result[0] : null;
    }

    public static function GetAllFiltered(string $invoiceListJSON)
    {
        $oInvoices  = json_decode($invoiceListJSON);
        $Invoices   = [];

        $Invoices_Detail    = [];
        $oData              = [];
        $val                = -1;
        $indice             = -1;
        $isigv              = 0;

        foreach ($oInvoices as $k => $inv) {
            $oParam = array($inv->Id);
            $oData = DB::select('call sp_invoice_detail_list(?)', $oParam);
            $oData = self::filterHotelIncluded($oData);
            $Invoices_Detail[] = self::cloneData($oData);

            if (count($Invoices) == 0) {
                $Invoices = $oData;
                continue;
            }

            foreach ($oData as $i => $detail) {

                foreach ($Invoices as $j => $InvoicesElement) {
                    if ($InvoicesElement->InvoiceDetail_Type == 9) {
                        $isigv = 1;
                    }

                    if (self::isSameGroup($InvoicesElement, $detail)) {
                        $indice = $j;
                    }

                    if (self::isSameGroup($InvoicesElement, $detail)) {
                        if ($InvoicesElement->InvoiceDetail_Type != InvoiceDetailType::BookingTourPassenger) {
                            if ((!self::detailIsManual($InvoicesElement)
                                    && self::isSameInstanceAndStatus($InvoicesElement, $detail))
                                || (self::detailIsManual($InvoicesElement)
                                    && self::isSameDescription($InvoicesElement, $detail))
                            ) {
                                $val = $j;
                                break;
                            }
                        } else {
                            if (self::isSameDescription($InvoicesElement, $detail)) {
                                $val = $j;
                                break;
                            }
                        }
                    }
                }

                if ($val == -1) {
                    if ($indice == -1) {
                        if ($isigv == 0) {
                            array_push($Invoices, $detail);
                        } else {
                            array_splice($Invoices, count($Invoices) - 1, 0, array($detail));
                        }
                    } else {
                        array_splice($Invoices, $indice + 1, 0, array($detail));
                    }
                } else {
                    $InvoicesElement->InvoiceDetail_Total += $detail->InvoiceDetail_Total;
                    if ($InvoicesElement->InvoiceDetail_Type != InvoiceDetailType::Igv) {
                        $InvoicesElement->InvoiceDetail_Quantity += $detail->InvoiceDetail_Quantity;
                    }
                }
                $val = -1;
                $indice = -1;
            }
        }

        for ($k = 0; $k < count($Invoices); $k++) {
            for ($i = 0; $i < count($Invoices); $i++) {
                if (
                    $Invoices[$k]->Id == $Invoices[$i]->Id
                    && $Invoices[$k]->Id_InvoiceDetail != $Invoices[$i]->Id_InvoiceDetail
                    && self::isSameDescription($Invoices[$k], $Invoices[$i])
                ) {
                    $Invoices[$k]->InvoiceDetail_Quantity += $Invoices[$i]->InvoiceDetail_Quantity;
                    $Invoices[$k]->InvoiceDetail_Total += $Invoices[$i]->InvoiceDetail_Total;
                    array_splice($Invoices, $i, 1);
                }
            }
        }

        $oData = [];
        $oData["Invoices"] = $Invoices;
        $oData["Detail"] = $Invoices_Detail;

        return $oData;
    }
}
