<?php

namespace App\Classes\Modules\PackingLists\Processors;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Orders\Services\FetchesDataFromYDPortal;

use App\Classes\Modules\Schedules\DataTransferObjects\ScheduleObject;
use App\Classes\Modules\Schedules\Services\CreatesSchedule;
use App\Classes\Modules\Unity\Services\UpdatesContractObligation;
use App\Classes\Modules\PerfexCRM\Processors\PackingListToPerfexCRMProcessor;
use App\Classes\Notifications\ShipmentRescheduleEmail;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\PerfexCRMTasksYDStages;
use App\Models\Container;
use App\Models\Order;
use App\Models\PackingList;
use Carbon\Carbon;

class FetchContainersUpdatesFromYdPortalProcessor
{
    /** @var FetchesDataFromYDPortal */
    private $fetchesDataFRomYDPortal;

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

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

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

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


    /**
     * @return void
     * @throws MalformedRequestException
     */
    public function execute()
    {
        $containers = Container::where('status', ApprovalStatus::PENDING_VERIFICATION)->get();

        foreach ($containers as $container) {
            $time_start = microtime(true);

            $packingList = $container->packingLists()->first();

            $trackingRequest = $this->fetchesDataFRomYDPortal->clientRequest('http://www.yd-wl.com/api/ApiTracking.ashx', 'GET', [
                'trakingno' => $packingList->reference
            ]);

            $rows = $this->fetchesDataFRomYDPortal->getResponseBody($trackingRequest);

            if(!$rows->data){
                continue;
            }

            $unstuffingDate = null;
            $delayDate = null;
            $checkShipSailDate = null;
            $checkShipArrivalDate = null;

            foreach (array_reverse($rows->data) as $trackingRow) {
                $rescheduleETD = strpos($trackingRow->remark, '开') || strpos($trackingRow->remark, '到港');
                $rescheduleETA = strpos($trackingRow->remark, '到港');


                if (($rescheduleETD !== false || $rescheduleETA !== false) && strpos($trackingRow->tracking, '货物装柜完成。') === false) {
                    preg_match_all('/([0-9]+.{3})/', $trackingRow->remark, $matches);
                    $dates = collect();

                    foreach($matches[0] as $date){
                        try {
                            $dates->push(Carbon::parse(str_replace('.', '/', $date).Carbon::now()->format('Y')));
                        } catch (\Exception $exception) {
                            continue;
                        }

                    }

                    $rescheduleDate = $dates->sortDesc()->first();

                    if(!$delayDate || $rescheduleDate > $delayDate){
                        /** @var Carbon $delayDate */
                        $delayDate = $rescheduleDate;

                        if($rescheduleETA === false && $delayDate) {
                            $delayDate = $delayDate->addDays('5');
                        }
                    }

                }

                if ($trackingRow->tracking === '到港') {
                    $delayDate = Carbon::parse($trackingRow->trackingtime);
                    $checkShipArrivalDate = Carbon::parse($trackingRow->trackingtime);
                }

                if ($trackingRow->tracking === '已开船') {
                    $delayDate = Carbon::parse($trackingRow->trackingtime)->addDays('5');
                    $checkShipSailDate = Carbon::parse($trackingRow->trackingtime);
                }

                if ($trackingRow->tracking === '货物已进目的港仓库') {
                    $unstuffingDate = Carbon::parse($trackingRow->trackingtime);
                }
            }

            dump($delayDate, $unstuffingDate);

            if($checkShipSailDate){
                //check if ship has sailed
                if($checkShipSailDate->isPast()){
                    foreach ($container->packingLists as $packingList){
                        if(config('perfexcrm.is_enabled') == 'true'){
                            $this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::SHIP_SAILED);
                        }
                    }
                }
            }

            if($checkShipArrivalDate){
                //check if ship has arrived at destination
                if($checkShipArrivalDate->isPast()){
                    foreach ($container->packingLists as $packingList){
                        if(config('perfexcrm.is_enabled') == 'true'){
                            $this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::SHIP_ARRIVED);
                        }
                    }
                }
            }

            if($delayDate){

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

                if(!$transport->schedules()->whereDate('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));
                    foreach ($container->packingLists as $packingList){
                        if(!($packingList->owner instanceof Order)) continue;
                        $user = $packingList->owner->companyModule->employees()->first();
                        if(app()->environment(['production'])) {
                            $user->notify(new ShipmentRescheduleEmail($user, $packingList));
                        }
                    }
                }
            }

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

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

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

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

                    $signature = $packingList->owner->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->appointee->entity_sigiture;
                    foreach($packingList->steps()->where('reference', '!=', 'DELIVERY')->get() as $step){
                        $this->updatesContractObligations->execute($signature, $step->obligation_hash_id);
                        $step->update(['status' => ApprovalStatus::COMPLETED]);
                    }
                    if(config('perfexcrm.is_enabled') == 'true'){
                        $this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::UNSTUFFING);
                    }
                }
            }

            $time_end = microtime(true);

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

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

    }
}

