<?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\PackageObject;
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\DataTransferObjects\ScheduleObject;
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\DataTransferObjects\TransportObject;
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\PackageType;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransportType;
use App\Models\Order;
use App\Models\PackingList;
use App\Models\Transport;
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){

        try {
            $start = $start ? $start : Carbon::today()->subDays(5);

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

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

            $end = $end ? $end : Carbon::today()->addDay();
            $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[12].'"]';
                $containerDetailRequest = $this->fetchesDataFRomVTPortal->clientRequest('http://portal.vtnation.com.my/Services/DataControllerService.asmx/GetPage', 'POST', json_decode('{"controller":"ParcelInContainer","view":"grid1","request":{"PageIndex":-1,"PageSize":10000,"SortExpression":null,"Filter":'.$filter.'}}'), '');

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

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

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

                $eta = Carbon::parse($containerInfo[4])->addDays(2);
                $etd = Carbon::parse($eta)->subDays(7);

                $delayDate = $containerInfo[7];

                $containerStatus = $containerInfo[10];

                $unstuffingDate = $containerInfo[8];

                $transport = $container->transports()->first();

                if(!$transport || $eta !== $transport->eta){
                    $container->transports()->delete();

                    $transportObject = new TransportObject(TransportType::SEA, null, null,  $etd, null, ApprovalStatus::APPROVED);
                    /** @var Transport $transport */
                    $transport = $this->createsTransport->execute($transportObject, $container);
                    $this->createsSchedule->execute($transport, new ScheduleObject($etd, $eta, ApprovalStatus::APPROVED));
                }

                if($delayDate){
                    $delayDate = Carbon::parse($delayDate)->addDays(2);
                    $transport = $container->transports()->first();

                    if(!$transport->schedules()->where('eta', '=', $delayDate)->first()) {
                        $etd = $transport->schedules()->where('status', '=', ApprovalStatus::APPROVED)->first()->etd;
                        $transport->schedules()->update(['status' => ApprovalStatus::EXPIRED]);
                        $this->createsSchedule->execute($transport, new ScheduleObject($etd, $delayDate, ApprovalStatus::APPROVED));
                    }

                }

                if($containerStatus === 'Unstuffing'){
                    $container->update(['status' => ApprovalStatus::COMPLETED]);
                    $container->transports()->first()->update(['drop_date' => Carbon::parse($unstuffingDate)->subDay(), 'status' => ApprovalStatus::COMPLETED]);

                    /** @var PackingList $packingList */
                    foreach($container->packingLists as $packingList){

                        if($packingList->status === ApprovalStatus::PENDING_VERIFICATION){
                            $packingList->status = ApprovalStatus::APPROVED;
                            $packingList->save();
                        }

                    }
                }

                foreach($containerDetail->Rows as $packingList){
                    $marking = preg_split('(-|\(|\)|\/)', str_replace("/YW","", $packingList[39]));

                    $orderNumber = $marking[array_key_last($marking)];

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

                    $allow_contract = true;

                    try {
                        $warehousePackingList = $this->fetchesPackingList->execute(['reference' => $packingList[3]]);
                        if(!$warehousePackingList->owner instanceof Order) throw new ResourceNotFoundException;
                        $order = $warehousePackingList->owner;
                    } catch (ResourceNotFoundException $exception) {
                        try {
                            $order = $this->fetchesOrder->execute(['reference' => $orderNumber]);
                        } catch (ResourceNotFoundException $exception) {
                            try {
                                $order = $this->fetchesOrder->execute(['reference' => substr($orderNumber, -9)]);
                            } catch (ResourceNotFoundException $exception) {
                                $order = $this->fetchesCompanyModule->execute(['id' => 1]);
                                $allow_contract = false;
                            }

                        }
                    }


                    $packingListReference = $packingList[3];
                    $package = $packingList;
                    $deliveryDate = $package[1] ? Carbon::parse($package[1]) : null;

                    try {
                        $packingList = $this->fetchesPackingList->execute(['reference' => $packingListReference, 'type' => PackingListType::SHIPPING_PACKING_LIST]);
                    } catch (ResourceNotFoundException $exception){
                        if ($allow_contract) {
                            $appointee_id = $order->orderRoles()->where('role_id', '=', OrderRoleTypes::ORIGIN_FREIGHT_FORWARDER)->first()->appointee->id;
//                            $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', '=', OrderRoleTypes::IMPORTER)->first()->update(['entity_hash_id' => $importerContractEntity->hash_id, 'entity_signature' => $importerContractEntity->entity_signature_hash_id]);
//
//                            $this->unityAssignContractEntity->execute($supervisorContractEntity->hash_id, $contractObligations);
                        } else {
                            $appointee_id = 2307;
                        }

                        $packingListObject = new PackingListObject($packingListReference, $appointee_id, PackingListType::SHIPPING_PACKING_LIST, ApprovalStatus::SUSPENDED, null);

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

                    $container->packingLists()->detach($packingList);
                    $container->packingLists()->attach($packingList);
                    $packingList->packages()->delete();

                    $replica = $packingList->packingLists()->where('type', PackingListType::SHIPPING_PACKING_LIST_REPLICA)->first();
                    if($replica) $replica->packages()->delete();

                    $packageObject = new PackageObject(PackageType::CARTON, $package[8], $package[21], $package[22], $package[20], 0, $package[11], ApprovalStatus::APPROVED);
                    $this->createPackageProcessor->execute($packageObject, $packingList);
                    if($containerStatus === 'Unstuffing'){
                        $packingList->transports()->delete();

                        if($deliveryDate) {
//                            && !$packingList->transports()->exists()
                            $packingList->status = ApprovalStatus::COMPLETED;
                            $packingList->save();

                            $transportObject = new TransportObject(TransportType::LAND, null, null, $deliveryDate, $deliveryDate, ApprovalStatus::APPROVED);
                            /** @var Transport $transport */
                            $transport = $this->createsTransport->execute($transportObject, $packingList);
                            $this->createsSchedule->execute($transport, new ScheduleObject($deliveryDate, $deliveryDate, ApprovalStatus::APPROVED));
                        }
                    }

                }

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

        return [];

    }


}
