<?php

namespace App\Providers;

use App\Http\Helpers\ParamsValidator;
use App\Modules\Credential\Domain\Credential;
use App\Modules\Group\Application\GroupSlaveList;
use App\Modules\Group\Application\WrapReport\RichProviderWithJson;
use App\Modules\Group\Domain\IdGroup;
use App\Modules\Group\Domain\IncludeSelf;
use App\Modules\Group\Infraestructure\MysqlGroupSlaveRepository;
use App\Modules\GroupPassenger\Application\SearchRoom\SearchRoomGroupPassengerQuery;
use App\Modules\GroupPassenger\Application\SearchRoom\SearchRoomGroupPassengerResponse;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class GroupReports
{

    public static function Report_Excel_Data(Request $Request, $oDomain)
    {
        $oResponse  = array();

        $oResponse = ParamsValidator::Validate_Request(
            $Request,
            [
                'Date' => 'required|string',
                'resume' => 'required|int'
            ],
            $oDomain
        );

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

            if (!$Request->input('DateEnd')) {
                $dateEnd = $Request->input('Date');
            } else {
                $dateEnd = $Request->input('DateEnd');
            }
            $oParam = array($Request->input("Date"), $dateEnd);

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

            $oData = self::mapGroups($oData, $Request->input("resume"));

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $oDomain;
            $oResponse["Response_Message"]          = $oDomain . " report";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }
        return $oResponse;
    }

    public static function Report_Excel_Group_Data(Request $Request, $oDomain)
    {
        $oResponse  = array();
        $oValParams = [
            'Date' => 'required|string',
            'Id_Group' => 'required|int',
            'resume' => 'required|int'
        ];

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            if (!$Request->input('DateEnd')) {
                $dateEnd = $Request->input('Date');
            } else {
                $dateEnd = $Request->input('DateEnd');
            }
            $oParam = array($Request->input("Date"), $dateEnd);
            $oData  = DB::select('call sp_group_report(?,?)', $oParam);
            $oIdGroup = $Request->input('Id_Group');
            $oData = array_values(array_filter($oData, function ($oGroup) use ($oIdGroup) {
                return $oGroup->Id_Group == $oIdGroup;
            }));



            $oData = self::mapGroups($oData, $Request->input("resume"));

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $oDomain;
            $oResponse["Response_Message"]          = $oDomain . " report";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }
        return $oResponse;
    }

    public static function reportExcelDataIdGroup(Request $Request, $oDomain)
    {
        $oResponse  = array();
        $oValParams = ['Id_Group' => 'required|int'];

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

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = array($Request->input("Id_Group"));
            $oData  = DB::select('call sp_group_report_index(?)', $oParam);

            $oData = self::mapGroups($oData, 0);

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $oDomain;
            $oResponse["Response_Message"]          = $oDomain . " report";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }
        return $oResponse;
    }

    protected static function mapGroups($oGroups, $resume)
    {
        return array_map(function ($oGroup) use ($resume) {
            $oGroup->Passenger = array_map(
                self::richGroupPassenger($oGroup->Group_ServiceType),
                DB::select('call sp_group_passenger_report(?)', [$oGroup->Id_Group])
            );

            $oGroup->Credential = Credential::getPublic(DB::select('call sp_credential_list()', []));
            $oGroup->Group_Slaves = array_map(
                function ($slave) {
                    $slave->Providers = DB::select('call sp_group_provider_list_slave(?)', [$slave->Id_Group]);
                    $slave->ProvidersResume = DB::select('call sp_group_provider_list_resume(?)', [$slave->Id_Group]);
                    return $slave;
                },
                (new GroupSlaveList(new MysqlGroupSlaveRepository))(new IdGroup($oGroup->Id_Group), new IncludeSelf(IncludeSelf::ANSWER_NO))
            );


            $providers = DB::select('call sp_group_provider_list_slave(?)', [$oGroup->Id_Group]);
            $oGroup->Providers = $providers;

            $providers = DB::select('call sp_group_provider_list_resume(?)', [$oGroup->Id_Group]);

            $groupCount = 1 + count($oGroup->Group_Slaves);
            $oGroup->ProvidersResume = (new RichProviderWithJson($groupCount))->__invoke($providers);

            for ($k = 0; $k < count($oGroup->Group_Slaves); $k++) {
                $oGroup->Group_Slaves[$k]->Passenger = [];
            };

            $equipments = DB::select('call sp_group_equipment_list_all(?)', [$oGroup->Id_Group]);
            $oGroup->Equipment = $equipments;


            foreach ($oGroup->Equipment as $equipment) {
                if ($equipment->Id_GroupPassenger === 0) {
                    continue;
                }
                foreach ($oGroup->Passenger as $pass) {
                    $pass->FalseEquipment = array_filter(
                        $pass->FalseEquipment,
                        fn($falseEquipment) => $falseEquipment->Id_BookingEquipmentPassenger !== $equipment->Id_BookingEquipmentPassenger
                    );
                }
            }

            foreach ($equipments as $eq) {
                $eq->GroupEquipment_QuantityCounter = 0;
                foreach ($oGroup->Passenger as $pass) {
                    if ($pass->BookingTourPassenger_Status != 2) {
                        continue;
                    }
                    if ($eq->Id_GroupPassenger === 0) {
                        foreach ($pass->FalseEquipment as $fe) {
                            if ($fe->Id_Item == $eq->Id_Item &&  $fe->Id_Group == $eq->Id_Group) {
                                if ($eq->GroupEquipment_Quantity > $eq->GroupEquipment_QuantityCounter) {

                                    $Id_Item = $eq->Id_Item;

                                    $found = array_values(array_filter($pass->Equipments, function ($eq) use ($Id_Item) {
                                        return $eq->Id_Item == $Id_Item;
                                    }));
                                    if (count($found)) {
                                        $found[0]->Quantity++;
                                    } else {
                                        $pass->Equipments[] = (object) [
                                            'Id_GroupEquipment' => $eq->Id_GroupEquipment,
                                            'GroupEquipment_Quantity' => $eq->GroupEquipment_Quantity,
                                            'Id_Group' => $eq->Id_Group,
                                            'Id_Item' => $eq->Id_Item,
                                            'Item_Name' => $eq->Item_Name,
                                            'Item_Abrv' => $eq->Item_Abrv,
                                            'Item_Status' => $eq->Item_Status,
                                            'TypeItem_Name' => $eq->TypeItem_Name,
                                            'TypeItem_Abrv' => $eq->TypeItem_Abrv,
                                            'Quantity' => 1
                                        ];
                                    }
                                    $eq->GroupEquipment_QuantityCounter++;
                                }
                            }
                        }
                    } else {
                        if ($pass->Id_GroupPassenger === $eq->Id_GroupPassenger) {
                            $found = array_values(array_filter($pass->Equipments, fn($equipment) => $eq->Id_Item == $equipment->Id_Item));
                            if (count($found)) {
                                $found[0]->Quantity += $eq->GroupEquipment_Quantity;
                            } else {
                                $pass->Equipments[] = (object) [
                                    'Id_GroupEquipment' => $eq->Id_GroupEquipment,
                                    'GroupEquipment_Quantity' => $eq->GroupEquipment_Quantity,
                                    'Id_Group' => $eq->Id_Group,
                                    'Id_Item' => $eq->Id_Item,
                                    'Item_Name' => $eq->Item_Name,
                                    'Item_Abrv' => $eq->Item_Abrv,
                                    'Item_Status' => $eq->Item_Status,
                                    'TypeItem_Name' => $eq->TypeItem_Name,
                                    'TypeItem_Abrv' => $eq->TypeItem_Abrv,
                                    'Quantity' => 1
                                ];
                            }
                        }
                    }
                }
            }


            $oGroup->Passenger = array_map(function ($pass) {
                $pass->Passenger_Train_Remark1 = self::cutRemark($pass->Passenger_Train_Remark1);
                $pass->Passenger_Hotel = self::cutRemark($pass->Passenger_Hotel);
                $pass->Passenger_Entrance = self::cutRemark($pass->Passenger_Entrance);
                //$pass->Passenger_Train_Remark2 = self::cutRemark ($pass->Passenger_Train_Remark2);
                return $pass;
            }, $oGroup->Passenger);

            $len = count($oGroup->Passenger);
            $current = 1;

            for ($i = 0; $i < $len; $i++) {
                $p = $oGroup->Passenger[$i];

                if (!isset($p->CurrentId_Room)) {
                    $p->CurrentId_Room = $current;
                    $current++;
                }

                if ($p->Id_Room == null) {
                    continue;
                }

                for ($j = $i + 1; $j < $len; $j++) {
                    $auxP = $oGroup->Passenger[$j];
                    if ($auxP->Id_Room == $p->Id_Room && $oGroup->Id_Group && $auxP->Id_Group == $p->Id_Group) {
                        $auxP->CurrentId_Room = $p->CurrentId_Room;
                    }
                }
            }

            usort($oGroup->Passenger, fn($p, $q) => $p->CurrentId_Room - $q->CurrentId_Room);

            if ($oGroup->Group_ServiceType > 2) {
                $oGroup->Fligth = self::cutFlyes($oGroup->Service_Detail);
                if ($oGroup->Group_ServiceType > 3) {
                    $oGroup->BookingTourPassenger_Hotel = $oGroup->Fligth[2];
                } else {
                    $oGroup->BookingTourPassenger_Hotel = $oGroup->Fligth[1];
                }
            } else {
                $oGroup->Fligth = [];
            }
            if ($resume == 0) {
                self::PushPaxSlaves($oGroup);
            }

            return $oGroup;
        }, $oGroups);
    }

    protected static function richGroupPassenger($serviceType)
    {
        return function ($groupPassenger) use ($serviceType) {
            if ($serviceType >= 3) {
                $groupPassenger->FalseEquipment = [];
                $groupPassenger->Equipments = [];
                $groupPassenger->Rooms = [];
                return $groupPassenger;
            }
            $groupPassenger->FalseEquipment = DB::select('call sp_group_equipment_report_by_group_passenger(?)', [$groupPassenger->Id_GroupPassenger]);
            $groupPassenger->Equipments = [];

            /** @var QueryBus $queryBus */
            $queryBus = app()->make(QueryBus::class);
            /** @var SearchRoomGroupPassengerResponse $response */
            $response = $queryBus->ask(new SearchRoomGroupPassengerQuery($groupPassenger->Id_BookingTourPassenger));
            $room = $response->response();
            if ($room) {
                $groupPassenger->Id_Room = $room['Room']->Id_Room;
                $groupPassenger->Id_TypeRoom = $room['TypeRoom']->Id_TypeRoom;
                $groupPassenger->TypeRoom_Name = $room['TypeRoom']->TypeRoom_Name;
                $groupPassenger->TypeRoom_Abrv = $room['TypeRoom']->TypeRoom_Abrv;
            } else {
                $groupPassenger->Id_Room = null;
                $groupPassenger->Id_TypeRoom = null;
                $groupPassenger->TypeRoom_Name = null;
                $groupPassenger->TypeRoom_Abrv = null;
            }

            return $groupPassenger;
        };
    }

    public static function PushPaxSlaves($grups)
    {
        $Id = $grups->Id_Group;
        $index = 0;
        foreach ($grups->Passenger as $pax) {
            $index = self::SearchSlaves($grups->Group_Slaves, $pax->Id_Group);
            if ($index > -1 && $pax->Id_Group != $Id) {
                array_push($grups->Group_Slaves[$index]->Passenger, $pax);
            }
        }
        $grups->Passenger = array_values(array_filter($grups->Passenger, function ($gp) use ($Id) {
            return  $gp->Id_Group == $Id;
        }));
    }
    public static function SearchSlaves($slaves, $id_Group)
    {
        $i = 0;
        while ($i < count($slaves)) {
            if ($slaves[$i]->Id_Group == $id_Group) {
                return $i;
            }
            $i++;
        }
        return -1;
    }

    public static function cutRemark($remark)
    {
        $cutUpgrades = function ($remark) {
            return explode("\n", $remark);
        };
        $cutFeatures = function ($upgrades) {
            return array_reduce($upgrades, function ($carry, $val) {
                return array_merge($carry, explode('||', $val));
            }, []);
        };
        $cutElements = function ($features) {
            return array_reduce($features, function ($carry, $val) {
                $carry[] = implode(': ', explode('??', $val));
                return $carry;
            }, []);
        };

        $join = function ($elements) {
            return implode("\n", $elements);
        };

        return $join($cutElements($cutFeatures($cutUpgrades($remark))));
    }

    public static function cutFlyes($fly)
    {
        return explode("||", $fly);
    }

    public static function Report_Review_Data($Id, $oDomain)
    {
        $oResponse  = array();

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Id($Id, $oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = [$Id];
            $oData  = DB::select('call sp_group_index(?)', $oParam);

            $oData = array_map(function ($oGroup) {
                $Path_Thumb     = config("var.PATH_PUBLIC") . config("var.USER_COUNTRY_THUMB");


                $oPassengers  = DB::select('call sp_group_passenger_list(?,?,?,?)', [$oGroup->Id_Group, $Path_Thumb, '', '']);

                $oGroup->Passenger = array_map(function ($oPassenger) {

                    $oPassenger->Review = array_map(function ($oReview) {
                        $providers = [];
                        if ($oReview->PassengerReview_Provider != null) {
                            $providers = [$oReview->PassengerReview_Provider];
                        }

                        $oReview->Provider = array_reduce($providers, function ($carry, $pr) {
                            $carry = array_merge($carry, DB::select('call sp_provider_index(?)', [$pr]));
                            return $carry;
                        }, []);
                        return $oReview;
                    }, DB::select('call sp_passenger_review_list(?)', [$oPassenger->Id_Passenger]));
                    return $oPassenger;
                }, $oPassengers);
                return $oGroup;
            }, $oData);

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $oDomain;
            $oResponse["Response_Message"]          = $oDomain . " report";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }
        return $oResponse;
    }

    public static function Report_Passenger_Review_Data($Id_Passenger, $oDomain)
    {
        $oData = DB::select('call sp_passenger_index(?,?,?,?,?)', [$Id_Passenger, '', '', '', '']);

        $oData = array_map(function ($passenger) {
            $passenger->Review = array_map(function ($oReview) {
                $providers = explode('|', $oReview->PassengerReview_Provider);
                $providers = array_filter($providers, function ($pr) {
                    return !!$pr;
                });
                $oReview->Provider = array_reduce($providers, function ($carry, $pr) {
                    $carry = array_merge($carry, DB::select('call sp_provider_index(?)', [$pr]));
                    return $carry;
                }, []);
                return $oReview;
            }, DB::select('call sp_passenger_review_list(?)', [$passenger->Id_Passenger]));
            return $passenger;
        }, $oData);


        return $oData;
    }
}
