<?php

namespace App\Http\Controllers\Owner;

use App\Http\Controllers\Controller;
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\Domain\InvoiceDetailType;
use Illuminate\Support\Facades\DB;

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

    public function __construct()
    {
        $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',
            '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) {
            $oParam = array(
                $Request->input("InvoiceDetail_Quantity"),
                $Request->input("InvoiceDetail_Description"),
                $Request->input("InvoiceDetail_Price"),
                0,
                1,
                $Request->input("InvoiceDetail_Type"),
                100,
                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);

                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++) {
                $oParam = array(
                    $oDetails[$k]->InvoiceDetail_Quantity,
                    $oDetails[$k]->InvoiceDetail_Description,
                    $oDetails[$k]->InvoiceDetail_Price,
                    0,
                    1,
                    $oDetails[$k]->InvoiceDetail_Type,
                    100,
                    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);

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

    public function Update(Request $Request)
    {
        $oResponse  = array();
        $oValParams = array(
            'Id_InvoiceDetail'              => 'required|int',
            'InvoiceDetail_Price'           => 'required|numeric'
        );

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

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

            $oParam = array(
                $Request->input("Id_InvoiceDetail"),
                $Request->input("InvoiceDetail_Price")
            );
            $oData = DB::select('call sp_invoice_detail_update_admin(?,?)', $oParam);

            //  RESPONSE
            if ($oData[0]->Response_Success == 1) {
                $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'));

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

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

    public static function cloneData($data)
    {
        /** clone array of objects into new array of new objects */
        return array_map(
            fn ($item) => clone $item,
            $data
        );
    }

    public static function GetAll(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);
            $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;
    }
}
