<?php

namespace App\Classes\Modules\PackingLists\Processors;

use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Companies\Services\FetchesCompanyModule;
use App\Classes\Modules\Orders\Services\FetchesDataFromVTPortal;
use App\Classes\Modules\Orders\Services\FetchesOrder;
use App\Classes\Modules\PackingLists\DataTransferObjects\ContainerObject;
use App\Classes\Modules\PackingLists\DataTransferObjects\PackingListObject;
use App\Classes\Modules\PackingLists\Services\Containers\FetchesContainer;
use App\Classes\Modules\PackingLists\Services\FetchesPackingList;
use App\Classes\Modules\Schedules\Services\CreatesSchedule;
use App\Classes\Modules\Steps\DataTransferObjects\StepsObject;
use App\Classes\Modules\Steps\Services\CreatesStep;
use App\Classes\Modules\Transports\Services\CreatesTransport;
use App\Classes\Modules\Unity\Processors\ActivateContractProcessor;
use App\Classes\Modules\Unity\Processors\AssignContractEntityProcessor;
use App\Classes\Modules\Unity\Processors\CreateContractEntityProcessor;
use App\Classes\Modules\Unity\Services\CreatesContract;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\ContainerTypes;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Models\Order;
use App\Models\PackingList;
use Carbon\Carbon;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class FetchLoadedContainersFromVTPortalProcessor
{

    /** @var FetchesDataFromVTPortal */
    private $fetchesDataFRomVTPortal;

    /** @var FetchesOrder */
    private $fetchesOrder;

    /** @var CreatePackingListProcessor */
    private $createPackingListProcessor;

    /** @var CreatePackageProcessor */
    private $createPackageProcessor;

    /** @var CreatesTransport */
    private $createsTransport;

    /** @var CreatesSchedule */
    private $createsSchedule;

    /** @var FetchesContainer */
    private $fetchesContainer;

    /** @var FetchesPackingList */
    private $fetchesPackingList;

    /** @var FetchesCompanyModule */
    private $fetchesCompanyModule;

    /** @var CreateContainerProcessor */
    private $createContainerProcessor;

    /** @var CreatesContract */
    private $unityCreateContract;

    /** @var AssignContractEntityProcessor */
    private $unityAssignContractEntity;

    /** @var CreateContractEntityProcessor */
    private $unityCreateContractEntity;

    /** @var CreatesStep */
    private $createsStep;

    /** @var ActivateContractProcessor */
    private $unityActivateContract;

    /**
     * FetchLoadedContainersFromVTPortalProcessor constructor.
     * @param FetchesDataFromVTPortal $fetchesDataFRomVTPortal
     * @param FetchesOrder $fetchesOrder
     * @param CreatePackingListProcessor $createPackingListProcessor
     * @param CreatePackageProcessor $createPackageProcessor
     * @param CreatesTransport $createsTransport
     * @param CreatesSchedule $createsSchedule
     * @param FetchesContainer $fetchesContainer
     * @param FetchesPackingList $fetchesPackingList
     * @param FetchesCompanyModule $fetchesCompanyModule
     * @param CreateContainerProcessor $createContainerProcessor
     * @param CreatesContract $unityCreateContract
     * @param AssignContractEntityProcessor $unityAssignContractEntity
     * @param CreateContractEntityProcessor $unityCreateContractEntity
     * @param CreatesStep $createsStep
     * @param ActivateContractProcessor $unityActivateContract
     */
    public function __construct(FetchesDataFromVTPortal $fetchesDataFRomVTPortal, FetchesOrder $fetchesOrder, CreatePackingListProcessor $createPackingListProcessor, CreatePackageProcessor $createPackageProcessor, CreatesTransport $createsTransport, CreatesSchedule $createsSchedule, FetchesContainer $fetchesContainer, FetchesPackingList $fetchesPackingList, FetchesCompanyModule $fetchesCompanyModule, CreateContainerProcessor $createContainerProcessor, CreatesContract $unityCreateContract, AssignContractEntityProcessor $unityAssignContractEntity, CreateContractEntityProcessor $unityCreateContractEntity, CreatesStep $createsStep, ActivateContractProcessor $unityActivateContract)
    {
        $this->fetchesDataFRomVTPortal = $fetchesDataFRomVTPortal;
        $this->fetchesOrder = $fetchesOrder;
        $this->createPackingListProcessor = $createPackingListProcessor;
        $this->createPackageProcessor = $createPackageProcessor;
        $this->createsTransport = $createsTransport;
        $this->createsSchedule = $createsSchedule;
        $this->fetchesContainer = $fetchesContainer;
        $this->fetchesPackingList = $fetchesPackingList;
        $this->fetchesCompanyModule = $fetchesCompanyModule;
        $this->createContainerProcessor = $createContainerProcessor;
        $this->unityCreateContract = $unityCreateContract;
        $this->unityAssignContractEntity = $unityAssignContractEntity;
        $this->unityCreateContractEntity = $unityCreateContractEntity;
        $this->createsStep = $createsStep;
        $this->unityActivateContract = $unityActivateContract;
    }


    /**
     * @param Carbon|null $start
     * @param Carbon|null $end
     * @return array
     * @throws GuzzleException
     */
    public function execute(?Carbon $start = null, ?Carbon $end = null){

        DB::beginTransaction();
        try {
            $start = $start ? $start : Carbon::now()->subDays(10);

            $startLimit = Carbon::parse('11-08-2021');

            if($start->isBefore($startLimit)){
                $start = $startLimit;
            }

            $end = $end ? $end : Carbon::now();
            $filter = '["LoadedTime:$between$%js%\"'.$start->format('Y-m-d').'T00:00:00.000\"$and$%js%\"'.$end->format('Y-m-d').'T00:00:00.000\"\u0000"]';

            $containersRequest = $this->fetchesDataFRomVTPortal->clientRequest('http://portal.vtnation.com.my/Services/DataControllerService.asmx/GetPage', 'POST', json_decode('{"controller":"VCustomercontainer","view":"grid1","request":{"PageIndex":-1,"PageSize":10000,"SortExpression":"LoadedTime asc","Filter":'.$filter.'}}'), '');

            $containers = $this->fetchesDataFRomVTPortal->getResponseBody($containersRequest);

            foreach ($containers->Rows as $container){

                $filter = '["ContainerID:=%js%'.$container[11].'"]';
                $containerDetailRequest = $this->fetchesDataFRomVTPortal->clientRequest('http://portal.vtnation.com.my/Services/DataControllerService.asmx/GetPage', 'POST', json_decode('{"controller":"VPodetailparcel","view":"grid1","request":{"PageIndex":-1,"PageSize":10000,"SortExpression":"CreatedOn asc","Filter":'.$filter.'}}'), '');

                $containerDetail = $this->fetchesDataFRomVTPortal->getResponseBody($containerDetailRequest);

                $containerObject = new ContainerObject($container[0], str_replace(' ', '', $container[24]), str_replace(' ', '', $container[15]), ContainerTypes::FORTY_FEET_DRY_CONTAINER, Carbon::parse($container[5]), ApprovalStatus::PENDING_VERIFICATION);

                try {
                    $container = $this->fetchesContainer->execute(['reference' => $containerObject->getReference()]);
                } catch (ResourceNotFoundException $exception){
                    $warehouseId = $container[12];
                    $originWarehouse = $this->fetchesCompanyModule->execute(['id' => $warehouseId === 11 ? 3 : 4]);
                    $container = $this->createContainerProcessor->execute($containerObject, $originWarehouse);
                }

                foreach($containerDetail->Rows as $packingList){
                    $marking = explode('/', explode('CIEF/', $packingList[13])[1]);

                    if(!array_key_exists(1, $marking)){
                        continue;
                    }

                    $orderNumber = $marking[1];

                    if(!$packingList[22]){
                        continue;
                    }

                    try {
                        /** @var Order $order */
                        $order = $this->fetchesOrder->execute(['reference' => $orderNumber]);
                    } catch (ResourceNotFoundException $exception){
                        continue;
                    }

                    $packingListReference = $packingList[16];

                    try{
                        $this->fetchesPackingList->execute(['reference' => $packingListReference]);
                    } catch (ResourceNotFoundException $exception){
                        $contract = $this->unityCreateContract->execute();
                        $contractReference = $contract->hash_id;
                        $contractObligations = $contract->contract_obligation_list;

                        $this->unityActivateContract->execute($contractReference);

                        $supervisorHashId = $order->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->appointee->unity_hash_id;

                        $supervisorContractEntity = $this->unityCreateContractEntity->execute($contractReference, $supervisorHashId);
                        $order->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->update(['entity_hash_id' => $supervisorContractEntity->hash_id, 'entity_signature' => $supervisorContractEntity->entity_signature_hash_id]);

                        $importerContractEntity = $this->unityCreateContractEntity->execute($contractReference, $order->orderRoles()->where('role_id', '=', OrderRoleTypes::IMPORTER)->first()->appointee->unity_hash_id);
                        $order->orderRoles()->where('role_id', '=', RoleT::IMPORTER)->first()->update(['entity_hash_id' => $importerContractEntity->hash_id, 'entity_signature' => $importerContractEntity->entity_signature_hash_id]);

                        $this->unityAssignContractEntity->execute($supervisorContractEntity->hash_id, $contractObligations);

                        $connection = $order->companyModule->connections()->first();
                        $marking = $connection->invitee_reference;

                        $packingListObject = new PackingListObject($packingListReference, $order->orderRoles()->where('role_id', '=', OrderRoleTypes::ORIGIN_FREIGHT_FORWARDER)->first()->appointee->id, PackingListType::SHIPPING_PACKING_LIST, (int) filter_var($marking, FILTER_SANITIZE_NUMBER_INT) <= 1000 ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::SUSPENDED, $contractReference);

                        /** @var PackingList $packingList */
                        $packingList = $this->createPackingListProcessor->execute($packingListObject, $order);
                        $container->packingLists()->attach($packingList);

                        //Duplicate a copy for internal reference
                        $packingListObject = new PackingListObject($packingListReference,  RoleTypes::SUPER_ADMIN, PackingListType::SHIPPING_PACKING_LIST, (int) filter_var($marking, FILTER_SANITIZE_NUMBER_INT) <= 1000 ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::SUSPENDED, $contractReference);
                        $packingList = $this->createPackingListProcessor->execute($packingListObject, $packingList);

                        foreach($contractObligations as $obligation) {
                            $stepObject = new StepsObject($order->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->appointee->id, $obligation->reference, $obligation->sequence, $obligation->hash_id);
                            $this->createsStep->execute($packingList, $stepObject);
                        }


                    }

                }

            }
        } catch (\Exception $exception){
            Log::error($exception);
        }


        DB::commit();
        return [];

    }


}
