<?php

namespace App\Classes\Modules\PackingLists\Processors;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Schedules\DataTransferObjects\ScheduleObject;
use App\Classes\Modules\Schedules\Services\CreatesSchedule;
use App\Classes\Modules\Transports\DataTransferObjects\TransportObject;
use App\Classes\Modules\Transports\Services\CreatesTransport;
use App\Classes\Modules\Unity\Services\UpdatesContractObligation;
use App\Classes\Modules\PerfexCRM\Processors\PackingListToPerfexCRMProcessor;
use App\Classes\ValueObjects\Constants\PerfexCRMTasksYDStages;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransportType;
use App\Classes\ValueObjects\Constants\WarehouseReferences;
use App\Models\CompanyModule;
use App\Models\Order;
use App\Models\PackingList;
use App\Models\Transport;
use Carbon\Carbon;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class FetchDeliveryUpdatesFromYdPortalProcessor
{

    /** @var UpdatesContractObligation */
    private $updatesContractObligations;

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

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

    /** @var PackingListToPerfexCRMProcessor */
    private $packingListToPerfexCRMProcessor;

    /**
     * @param UpdatesContractObligation $updatesContractObligations
     * @param CreatesTransport $createsTransport
     * @param CreatesSchedule $createsSchedule
     * @param PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor
     */
    public function __construct(UpdatesContractObligation $updatesContractObligations, CreatesTransport $createsTransport, CreatesSchedule $createsSchedule, PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor)
    {
        $this->updatesContractObligations = $updatesContractObligations;
        $this->createsTransport = $createsTransport;
        $this->createsSchedule = $createsSchedule;
        $this->packingListToPerfexCRMProcessor = $packingListToPerfexCRMProcessor;
    }


    /**
     * @return void
     * @throws MalformedRequestException
     * @throws GuzzleException
     */
    public function execute()
    {
        $packingLists = PackingList::where('type', PackingListType::SHIPPING_PACKING_LIST)->whereRaw('LENGTH(reference) > 12')->whereHas('containers', function ($container){
            return $container->where('status', 3);
        })->doesntHave('transports')->get()->reverse();
        foreach ($packingLists->chunk(50) as $chunk) {


            foreach ($chunk as $packingList){
                if(!$packingList->reference) continue;
                $time_start = microtime(true);

                $deliveryDate = null;

                try {
                    $client = new \GuzzleHttp\Client(['cookies' => true, 'headers' => ['Cookie' => 'utc_offset=480']]);

                    $request = $client->request('get', 'https://main.universe.com.my/Tracking/User/Paging?sEcho=1&sTrackingNo='.$packingList->reference.'&sOrgId=sti', ['timeout' => 3]);
                    $deliveryTracking = json_decode($request->getBody()->getContents());
                    foreach (array_reverse($deliveryTracking->aaData) as $trackingRow) {
                        $trackingDate = Carbon::createFromFormat('d/m/y H:i', $trackingRow->LocalDateTime);
                        if (Str::contains($trackingRow->PublicDescription, ['delivered'])) {
                            $deliveryDate = $trackingDate;
                        }
                    }
                } catch (\Exception $exception){
                    dump($exception);
                    Log::debug('failed to fetch delivery tracking');
                }

                if($deliveryDate){
                    $packingList->status = ApprovalStatus::COMPLETED;
                    $packingList->save();

                    $deliveryDate = Carbon::parse($deliveryDate);
                    $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));

                    if(!($packingList->owner instanceof Order)) continue;

                    $deliveryStep = $packingList->steps()->where('reference', '=', 'LAST_MILE_DELIVERY')->first();
                    $signature = $packingList->owner->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->appointee->entity_sigiture;
                    $this->updatesContractObligations->execute($signature, $deliveryStep->obligation_hash_id);
                    $deliveryStep->update(['status' => ApprovalStatus::COMPLETED]);

                    if(config('perfexcrm.is_enabled') == 'true'){
                        $this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::DELIVERY);
                    }
                }

                $time_end = microtime(true);

                $execution_time = ($time_end - $time_start)/60;

                echo 'successful => <b>Total Execution Time:</b> '.$execution_time.' Mins<br>';
            }

        }

    }
}

