<?php

namespace App\Classes\Modules\PackingLists\Processors;

use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Addresses\Processors\CreateAddressFromOldAddressProcessor;
use App\Classes\Modules\Addresses\Services\CleansOldAddress;
use App\Classes\Modules\Companies\Services\FetchesCompanyModule;
use App\Classes\Modules\Orders\Processors\CreateOrderProcessor;
use App\Classes\Modules\Orders\Services\FetchesDataFromVTPortal;
use App\Classes\Modules\Orders\Services\FetchesOrder;
use App\Classes\Modules\PackingLists\ControllersLogic\Packages\CreatePackageLogic;
use App\Classes\Modules\PackingLists\DataTransferObjects\PackageObject;
use App\Classes\Modules\PackingLists\DataTransferObjects\PackingListObject;
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\Transports\DataTransferObjects\TransportObject;
use App\Classes\Modules\Transports\Services\CreatesTransport;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\PackageType;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransportType;
use App\Models\Address;
use App\Models\CompanyModule;
use App\Models\OldOrders;
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 FetchWarehouseReceiveListFromVTPortalProcessor
{


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

    /** @var CleansOldAddress */
    private $cleansOldAddress;

    /** @var CreateAddressFromOldAddressProcessor */
    private $createAddressFromOldAddressProcessor;

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

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

    /** @var CreateOrderProcessor */
    private $createOrderProcessor;

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

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

    /** @var CreatePackageLogic */
    private $createPackage;

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

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

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

    /**
     * FetchWarehouseReceiveListFromVTPortalProcessor constructor.
     * @param FetchesDataFromVTPortal $fetchesDataFRomVTPortal
     * @param CleansOldAddress $cleansOldAddress
     * @param CreateAddressFromOldAddressProcessor $createAddressFromOldAddressProcessor
     * @param FetchesCompanyModule $fetchesCompanyModule
     * @param FetchesOrder $fetchesOrder
     * @param CreateOrderProcessor $createOrderProcessor
     * @param FetchesPackingList $fetchesPakcingList
     * @param CreatePackingListProcessor $createPackingListProcessor
     * @param CreatePackageLogic $createPackage
     * @param CreatePackageProcessor $createPackageProcessor
     * @param CreatesTransport $createsTransport
     * @param CreatesSchedule $createsSchedule
     */
    public function __construct(FetchesDataFromVTPortal $fetchesDataFRomVTPortal, CleansOldAddress $cleansOldAddress, CreateAddressFromOldAddressProcessor $createAddressFromOldAddressProcessor, FetchesCompanyModule $fetchesCompanyModule, FetchesOrder $fetchesOrder, CreateOrderProcessor $createOrderProcessor, FetchesPackingList $fetchesPakcingList, CreatePackingListProcessor $createPackingListProcessor, CreatePackageLogic $createPackage, CreatePackageProcessor $createPackageProcessor, CreatesTransport $createsTransport, CreatesSchedule $createsSchedule)
    {
        $this->fetchesDataFRomVTPortal = $fetchesDataFRomVTPortal;
        $this->cleansOldAddress = $cleansOldAddress;
        $this->createAddressFromOldAddressProcessor = $createAddressFromOldAddressProcessor;
        $this->fetchesCompanyModule = $fetchesCompanyModule;
        $this->fetchesOrder = $fetchesOrder;
        $this->createOrderProcessor = $createOrderProcessor;
        $this->fetchesPackingList = $fetchesPakcingList;
        $this->createPackingListProcessor = $createPackingListProcessor;
        $this->createPackage = $createPackage;
        $this->createPackageProcessor = $createPackageProcessor;
        $this->createsTransport = $createsTransport;
        $this->createsSchedule = $createsSchedule;
    }


    /**
     * @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()->subMonth();

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

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

            $end = $end ? $end : Carbon::now();

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

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

            $response = $this->fetchesDataFRomVTPortal->getResponseBody($warehouseListRequest);

            foreach ($response->Rows as $parcel){

                $marking = explode('/', explode('CIEF/', $parcel[5])[1]);

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

                $orderNumber = $marking[1];

                if(!$parcel[9]){
                    continue;
                }


                try {
                    $order = $this->fetchesOrder->execute(['reference' => $orderNumber]);
                } catch (ResourceNotFoundException $exception) {

                    $oldOrder = OldOrders::where('marking', '=', $orderNumber)->first();

                    if(!$oldOrder){
                        continue;
                    }


                    $customerMarking = str_replace('/', '', str_replace(' ', '', explode('CIEF/', $oldOrder->company->marking)[1]));

                    $companyModule = CompanyModule::whereHas('inviters', function($query) use ($customerMarking) {
                        return $query->where('invitee_reference', '=', $customerMarking);
                    })->first();


                    if(!$companyModule){
                        log::error('unknown customer: '.$customerMarking);
                        continue;
                    }

                    if(!$oldOrder->address) {
                        continue;
                    }

                    /** @var Address $address */
                    $address = $this->createAddressFromOldAddressProcessor->execute($oldOrder->address, $companyModule);

                    if(!$address){
                        continue;
                    }


                    $warehouseId = $parcel[0];
                    $originWarehouse = $this->fetchesCompanyModule->execute(['id' => $warehouseId === 11 ? 3 : 4]);

                    $order = $this->createOrderProcessor->execute($companyModule->company, $originWarehouse, $address, $orderNumber);

                    $order->created_at = Carbon::parse($parcel[1]);
                    $order->save();

                }

                $packingListReference = $parcel[17];
                $quantity = $parcel[9];
                $measurement = round(($parcel[10]/$quantity) ** (1/3) * 100, 2);
                $description = $parcel[7];
                $tracking = $parcel[6];
                $receiveDate = $parcel[1];

                $packingListObject = new PackingListObject($packingListReference, $order->orderRoles()->where('role_id', '=', OrderRoleTypes::ORIGIN_WAREHOUSE)->first()->appointee->id, PackingListType::WAREHOUSE_RECEIVE_LIST, ApprovalStatus::APPROVED);

                /** @var PackingList $packingList */
                $packingList = $order->packingLists()->where('reference', '=', $packingListReference)->first();

                if(!$packingList) {
                    $packingList = $this->createPackingListProcessor->execute($packingListObject, $order);

                    $transportObject = new TransportObject(TransportType::LAND, null, $tracking, Carbon::parse($receiveDate), Carbon::parse($receiveDate), ApprovalStatus::APPROVED);


                    $this->createsTransport->execute($transportObject, $packingList);
                }

                /** @var Transport $transport */
                $transport = $packingList->transports()->first();

                $transport->schedules()->update(['status' => ApprovalStatus::EXPIRED]);

                $this->createsSchedule->execute($transport, new ScheduleObject(Carbon::parse($receiveDate), Carbon::parse($receiveDate), ApprovalStatus::APPROVED));

                $packingList->packages()->delete();

                $packageObject = new PackageObject(PackageType::CARTON, $description, $measurement, $measurement, $measurement, 0, $quantity, ApprovalStatus::APPROVED);
                $this->createPackageProcessor->execute($packageObject, $packingList);

            }


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

        DB::commit();
        return [];

    }


}
