<?php
namespace App\Library;
use App\Core\Service\Logger;
use App\Service\Api;

class BlueDart 
{
    private $api = null;
    private $customer_id;
    private $login_id;
    private $api_key_password;
    private $vendor_code;
    private $pincode_servicability_url;
    private $login_url = 'https://apigateway.bluedart.com/in/transportation/token/v1/login';
    private $docket_creation_url = 'https://apigateway.bluedart.com/in/transportation/waybill/v1/GenerateWayBill';
    private $api_tracking_url;

    public function __construct(Array $settings) {
        $this->api = new Api();
        $this->customer_id = !empty($settings['customer_id']) ? $settings['customer_id'] : '';
        $this->login_id = !empty($settings['login_id']) ? $settings['login_id'] : '';
        $this->api_key_password = !empty($settings['api_key_password']) ? $settings['api_key_password'] : '';
        $this->vendor_code = !empty($settings['vendor_code']) ? $settings['vendor_code'] : '';  
        $this->pincode_servicability_url = !empty($settings['pincode_servicability_url']) ? $settings['pincode_servicability_url'] : '';
        $this->api_tracking_url = !empty($settings['api_tracking_url']) ? trim($settings['api_tracking_url']) : '';
    }    
    
    private function checkConfiguration() {
        try {
            if(empty($this->customer_id) || empty($this->login_id) || empty($this->api_key_password) || empty($this->vendor_code)) {
                throw new \Exception("BlueDart configuration is incomplete. Provide all required settings.(customer_id, login_id, api_key_password, vendor_code and so on.)");
            }
            return true;
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return false;
        }
    }

    private function generateJWTtoken() {
        try {
            $headers = [
                'Content-Type: application/json',
                'ClientID:'.$this->api_key_password,
                'clientSecret:'.$this->vendor_code
            ];
            $callrs = $this->api->call($this->login_url, 'GET', $headers, '');
            if(empty($callrs['status'])) {
                throw new \Exception($callrs['error']);
            }
            if(empty($callrs['data'])) {
                throw new \Exception("No response from BlueDart login API");
            }
            $response = json_decode($callrs['data'], true);
            if (!empty($response['JWTToken'])) {
                return ["status"=>true, "data"=>$response['JWTToken']];
            } else {
                throw new \Exception('Token not found in response: ' . $callrs['data']);
            }
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>true, "error"=>$e->getMessage()];
        }
    }

    public function checkPincodeServiceability(String $pincode) {
        try {
            if(!$this->checkConfiguration()) {
                throw new \Exception("BlueDart configuration is incomplete. Provide all required settings.(customer_id, login_id, api_key_password, vendor_code and so on.)");
            }
            if(empty($this->pincode_servicability_url)) {
                throw new \Exception("Pincode serviceability URL is not configured");
            }
            if(empty($pincode)) {
                throw new \Exception("Pincode cannot be empty");
            }
            $tokenrs = $this->generateJWTtoken();
            if(empty($tokenrs['status'])) {
                throw new \Exception($tokenrs['error']);
            }
           
            $headers = ['Content-Type: application/json','JWTToken:'.$tokenrs['data']];
            $payload = json_encode([
                "pinCode" => $pincode,
                "profile" => ["LoginID" => $this->login_id, "Api_type" => "S", "LicenceKey" => $this->customer_id]
            ]);

            $callrs = $this->api->call(trim($this->pincode_servicability_url), 'POST', $headers, $payload);
            if(empty($callrs['status'])) {
                throw new \Exception($callrs['error']);
            }
            if(empty($callrs['data'])) {
                throw new \Exception("No response from BlueDart check pincode serviceability API");
            }
            $response = json_decode($callrs['data'], true);
            $isError = $response['GetServicesforPincodeResult']['IsError'];
            $eTailPrePaidAirInbound = $response['GetServicesforPincodeResult']['eTailPrePaidAirInbound'];
            $eTailCODAirInbound = $response['GetServicesforPincodeResult']['eTailCODAirInbound'];
            
            if ($isError === false && ($eTailPrePaidAirInbound === 'Yes' || $eTailCODAirInbound === 'Yes') ) {
                return array('status' => true, 'serviceable' => true);
            } else {
                return array('status' => true, 'serviceable' => false);
            }
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return null;
        }
    }

    public function createDocketNo($order, $item, $data) {
        try {
            if(!$this->checkConfiguration()) {
                throw new \Exception("BlueDart configuration is incomplete. Provide all required settings.(customer_id, login_id, api_key_password, vendor_code and so on.)");
            }
            
            $resp = $this->prepareDataForDocketNo($order, $item, $data);
            if(empty($resp['status'])) {
                return ["status"=>false, "error"=>$resp['error']];
            }
            $payload = json_encode($resp['data']);
            $tokenrs = $this->generateJWTtoken();
            if(empty($tokenrs['status'])) {
                throw new \Exception($tokenrs['error']);
            }
           
            $headers = ['Content-Type: application/json','JWTToken:'.$tokenrs['data']];

            $callrs = $this->api->call(trim($this->docket_creation_url), 'POST', $headers, $payload);
            if(empty($callrs['status'])) {
                throw new \Exception($callrs['error']);
            }
            if(empty($callrs['data'])) {
                throw new \Exception("No response from BlueDart check pincode serviceability API");
            }
            $response = json_decode($callrs['data'], true);
            if(!empty($response['error-response'])) {
                if(!empty($response['error-response'][0]['IsError'])){
                    $statusArray = $response['error-response'][0]['Status'] ?? [];
                    $messages = array_map(function ($status) {
                        return $status['StatusInformation'] ?? 'Unknown error';
                    }, $statusArray);
                    $error_message = implode("; ", $messages);
                    throw new \Exception("Error in creating docket no API: " . $error_message);
                }     
            }
            if (empty($response['GenerateWayBillResult'])) {
                throw new \Exception("Error in creating docket no API: AWB not generated");
            } else if(empty($response['GenerateWayBillResult']['AWBNo'])) {
                throw new \Exception("Error in creating docket no API: AWB not generated");
            }
            return array('status' => true, 'data' => $response['GenerateWayBillResult']);
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>false, "error"=>$e->getMessage()];
        }
    }

    public function trackDocketNo(String $docket_no) {
        try {
            if(empty($docket_no)) {
                throw new \Exception("Provide Docket No to track the shipment for BlueDart");
            }
            if(!$this->checkConfiguration()) {
                throw new \Exception("BlueDart configuration is incomplete. Provide all required settings.(customer_id, login_id, api_key_password, vendor_code and so on.)");
            }
            if(empty($this->api_tracking_url)) {
                throw new \Exception("Courier tracking URL is not configured for BlueDart");
            }
            $url = str_replace(array('{{docket_no}}', '{{login_id}}', '{{license_key}}'), array($docket_no, $this->login_id, $this->customer_id), $this->api_tracking_url);
            $callrs = $this->api->call($url, 'GET', [], '');
            if(empty($callrs['status'])) {
                throw new \Exception($callrs['error']);
            }
            if(empty($callrs['data'])) {
                throw new \Exception("No response from BlueDart track courier API");
            }
            $resp = $this->prepareCoutrierTrackingResponse($docket_no, $callrs['data'], 'BlueDart');
            if(empty($resp['status'])) {
                throw new \Exception($resp['error']);
            }
            return array('status' => true, 'data' => $resp['data']);
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>false, "error"=>$e->getMessage()];
        }
    }

    private function prepareDataForDocketNo($order, $item, $data) {
        try {
            $igst = $cgst = $sgst = 0.00;
            $igstP = $cgstP = $sgstP = 0.00;
            if ($order['branch_state_name'] == $order['customer_address_detail']['address_state_name']) {
                $cgst = sprintf("%.2f", ($order['gross_amt'] - $order['tax_amt']) / 2);
                $sgst = sprintf("%.2f", ($order['gross_amt'] - $order['tax_amt']) / 2);

                $cgstP = sprintf("%.2f", !empty($item['tax_percentage']) ? $item['tax_percentage'] / 2 : 0);
                $sgstP = sprintf("%.2f", !empty($item['tax_percentage']) ? $item['tax_percentage'] / 2 : 0);
            } else {
                $igst = sprintf("%.2f", ($order['gross_amt'] - $order['tax_amt']));
                $igstP = sprintf("%.2f", !empty($item['tax_percentage']) ? $item['tax_percentage'] : 0, 2);
            }

            $dt = [
                'Request'=> [
                    'Consignee'=>[],
                    'Returnadds'=>[],
                    'Services'=>[],
                    'Shipper'=>[]    
                ],
                'Profile'=>[]
            ];
            $dt['Request']['Consignee']['AvailableDays'] = "";
            $dt['Request']['Consignee']['AvailableTiming'] = "";
            $dt['Request']['Consignee']['ConsigneeAddress1'] = $order['customer_address_detail']['address'];
            $dt['Request']['Consignee']['ConsigneeAddress2'] = $order['customer_address_detail']['address_city_name'];
            $dt['Request']['Consignee']['ConsigneeAddress3'] = $order['customer_address_detail']['address_state_name'];
            $dt['Request']['Consignee']['ConsigneeAddressType'] = '';
            $dt['Request']['Consignee']['ConsigneeAddressinfo'] = '';
            $dt['Request']['Consignee']['ConsigneeAttention']   = '';
            $dt['Request']['Consignee']['ConsigneeEmailID']  = !empty($order['customer_detail']['email_id'])?$order['customer_detail']['email_id']:'';
            $dt['Request']['Consignee']['ConsigneeGSTNumber'] = $order['customer_address_detail']['gst']?$order['customer_address_detail']['gst']:'';
            $dt['Request']['Consignee']['ConsigneeLatitude']  = '';
            $dt['Request']['Consignee']['ConsigneeLongitude'] = '';
            $dt['Request']['Consignee']['ConsigneeMaskedContactNumber'] = '';
            $dt['Request']['Consignee']['ConsigneeMobile'] = !empty($order['customer_detail']['mobile_number']) ? $order['customer_detail']['mobile_number'] : ''  ;
            $dt['Request']['Consignee']['ConsigneeName'] = !empty($order['customer_detail']['customer_name'])?$order['customer_detail']['customer_name']:'';
            $dt['Request']['Consignee']['ConsigneePincode'] = $order['customer_address_detail']['address_pincode']?$order['customer_address_detail']['address_pincode']:'';
            $dt['Request']['Consignee']['ConsigneeTelephone'] = '';

            $dt['Request']['Returnadds']['ManifestNumber'] = '';
            $dt['Request']['Returnadds']['ReturnAddress1'] = $order['branch_branch_address'] . " - " . $order['branch_pincode'];
            $dt['Request']['Returnadds']['ReturnAddress2'] = $order['branch_city_name'];
            $dt['Request']['Returnadds']['ReturnAddress3'] = $order['branch_state_name'];
            $dt['Request']['Returnadds']['ReturnAddressinfo'] = '';
            $dt['Request']['Returnadds']['ReturnContact']  = $order['company_name'];
            $dt['Request']['Returnadds']['ReturnEmailID']  = '';
            $dt['Request']['Returnadds']['ReturnLatitude'] = '';
            $dt['Request']['Returnadds']['ReturnLongitude'] = '';
            $dt['Request']['Returnadds']['ReturnMaskedContactNumber'] = '';
            $dt['Request']['Returnadds']['ReturnMobile'] = $order['phone'];
            $dt['Request']['Returnadds']['ReturnPincode'] = $order['branch_pincode'];
            $dt['Request']['Returnadds']['ReturnTelephone'] = '';

            $dt['Request']['Services']['AWBNo'] = '';
		    $dt['Request']['Services']['ActualWeight'] = round($data['courier_weight'], 2);
		    $dt['Request']['Services']['CollectableAmount'] = sprintf("%.2f", $order['net_amt']);
		    $dt['Request']['Services']['Commodity'] = [
                "CommodityDetail1" => "General",
                "CommodityDetail2" => "",
                "CommodityDetail3" => ""
            ];
            $dt['Request']['Services']['CreditReferenceNo'] = $order['id'];
            $dt['Request']['Services']['DeclaredValue'] = sprintf("%.2f", $order['net_amt']);

            $length = !empty($data['courier_length']) && $data['courier_length'] > 0 ? $data['courier_length'] : 9.0;
            $breadth = !empty($data['courier_breath']) && $data['courier_breath'] > 0 ? $data['courier_breath'] : 9.0;
            $height = !empty($data['courier_height']) && $data['courier_height'] > 0 ? $data['courier_height'] : 9.0;

            $dt['Request']['Services']['Dimensions'] = [
                [
                    'Length' => sprintf("%.2f", $length),
                    'Breadth' => sprintf("%.2f", $breadth),
                    'Height' => sprintf("%.2f", $height),
                    'Count' => 1
                ]
            ];

            $dt['Request']['Services']['PDFOutputNotRequired'] = true;
            $dt['Request']['Services']['PackType'] = '';
            $dt['Request']['Services']['PickupDate'] = '/Date(' . (strtotime($data['pickup_date']) * 1000) . ')/';
            $dt['Request']['Services']['PickupTime'] = sprintf("%02d%02d", $data['pickup_hour'], $data['pickup_minutes']);
            $dt['Request']['Services']['PieceCount'] = '1';
            $dt['Request']['Services']['ProductCode'] = 'A';
            $dt['Request']['Services']['ProductType'] = $data['product_type'] == "Dutiables" ? '1' : '2';
            $dt['Request']['Services']['RegisterPickup'] = true;
            $dt['Request']['Services']['SpecialInstruction'] = '';
            $dt['Request']['Services']['SubProductCode'] = $data['courier_payment_mode'] == 'c' ? 'C' : 'P';
            $dt['Request']['Services']['OTPBasedDelivery'] = 1;
            $dt['Request']['Services']['OTPCode'] = '';

            $dt['Request']['Services']['InvoiceNo'] = preg_replace('/[\W]/', '', $order['invoice_number']);

            $item_detail = json_decode($item['item_detail'], true);
            $itemId = $item_detail['item_id'];

            $dt['Request']['Services']['itemdtl'] = [
                [
                    "ItemDetails" => [
                        "CGSTAmount" => sprintf("%.2f", $cgst),
                        "HSCode" => $item['hsn_code'],
                        "IGSTAmount" => sprintf("%.2f", $igst),
                        "Instruction" => '',
                        "InvoiceDate" => !empty($order['invoice_date']) ? date('Y-m-d', strtotime($order['invoice_date'])) : date('Y-m-d'),
                        "InvoiceNumber" => $order['invoice_number'],
                        "ItemID" => $itemId,
                        "ItemName" => $data['courier_product_name'],
                        "ItemValue" => sprintf("%.2f", $order['gross_amt'] - $order['tax_amt']),
                        "Itemquantity" => 1,
                        "PlaceofSupply" => '',
                        "ProductDesc1" => '',
                        "ProductDesc2" => '',
                        "ReturnReason" => '',
                        "SGSTAmount" => sprintf("%.2f", $sgst),
                        "SKUNumber" => $item['item_code'] ?? '',
                        "SellerGSTNNumber" => $order['branch_gstin'],
                        "SellerName" => $order['company_name'],
                        "SubProduct1" => '',
                        "SubProduct2" => '',
                        "SubProduct3" => '',
                        "SubProduct4" => '',
                        "TaxableAmount" => sprintf("%.2f", $order['gross_amt'] - $order['tax_amt']),
                        "TotalValue" => sprintf("%.2f", $order['net_amt']),
                        "countryOfOrigin" => 'IN',
                        "docType" => 'INV',
                        "subSupplyType" => 1,
                        "supplyType" => 'I'
                    ]
                ]
            ];

            $dt['Request']['Shipper']['CustomerAddress1'] = $order['branch_branch_address'] . " -$order[branch_pincode]";
            $dt['Request']['Shipper']['CustomerAddress2'] = $order['city_name'];
            $dt['Request']['Shipper']['CustomerAddress3'] = $order['state_name'];
            $dt['Request']['Shipper']['CustomerCode'] = $this->vendor_code; //313390
            $dt['Request']['Shipper']['CustomerEmailID'] = !empty($order['branch_email']) ? $order['branch_email'] : '';
            $dt['Request']['Shipper']['CustomerGSTNumber'] = $order['branch_gstin'];
            $dt['Request']['Shipper']['CustomerLatitude'] = '';
            $dt['Request']['Shipper']['CustomerLongitude'] = '';
            $dt['Request']['Shipper']['CustomerMaskedContactNumber'] = '';
            $dt['Request']['Shipper']['CustomerMobile'] = @$order['phone'];
            $dt['Request']['Shipper']['CustomerName'] = $order['company_name'];
            $dt['Request']['Shipper']['CustomerPincode'] = $order['branch_pincode'];
            $dt['Request']['Shipper']['CustomerTelephone'] = '';
            $dt['Request']['Shipper']['IsToPayCustomer'] = false;
            $dt['Request']['Shipper']['OriginArea'] = $data['origin_area'] ?? "DEL";
            $dt['Request']['Shipper']['Sender'] = $order['company_name'];
            $dt['Request']['Shipper']['VendorCode'] = '';

            $dt['Profile']['Api_type'] = 'S';
            $dt['Profile']['LicenceKey'] = $this->customer_id;
            $dt['Profile']['LoginID'] = $this->login_id;

            return ["status"=>true, "data"=>$dt];
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>true, "error"=>$e->getMessage()];
        }
    }

    private function prepareCoutrierTrackingResponse($docket_number, $xml, $courier_name) {
        try {
            $dataArray = array();
            $doc = new \DOMDocument();
			$doc->loadXML($xml);
			$all = $doc->getElementsByTagName("Shipment");
			$detailArray = array();
            foreach ($all as $one) {   
                $dataArray['DOCKNO'] = $docket_number;

				$info = $one->getElementsByTagName("PickUpDate");
				$PickUpDate = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';
				$dataArray['EVENTDATE'] = $PickUpDate;

				$dataArray['ORDER_NO'] = '';

				$info = $one->getElementsByTagName("Origin");
				$ORIGIN = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';
				$dataArray['ORIGIN'] = $ORIGIN;

				$info = $one->getElementsByTagName("Destination");
				$title = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';
				$dataArray['DESTINATION'] = $title;

				$info = $one->getElementsByTagName("ConsigneePincode");
				$title = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';
				$dataArray['PINCODE'] = $title;

				$info = $one->getElementsByTagName("Status");
				$title = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';
				$dataArray['CURRENT_STATUS'] = $title;

				// Tracking Logs ********************************** STARTS
				$trackingLog = $one->getElementsByTagName("ScanDetail");
				foreach ($trackingLog as $key2 => $value) {
					$info = $value->getElementsByTagName("Scan");
					$status = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';

					$info = $value->getElementsByTagName("ScanDate");
					$ScanDate = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';

					$info = $value->getElementsByTagName("ScanTime");
					$ScanTime = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';

					$info = $value->getElementsByTagName("ScannedLocation");
					$ScannedLocation = isset($info->item(0)->nodeValue) ? $info->item(0)->nodeValue : '';

					$detailArray[] = array('CURRENT_CITY' => $ScannedLocation, 'EVENTDATE' => $ScanDate, 'EVENTTIME' => $ScanTime, 'NEXT_LOCATION' => '-', 'CURRENT_STATUS' => $status, 'datetime' => date('Y-m-d H:i', strtotime($ScanDate . " " . $ScanTime)));
				}
            }
            $datetimeArray = array_column($detailArray, 'datetime');
			array_multisort($datetimeArray, SORT_DESC, $detailArray);
			$dataArray['CURRENT_CITY'] = (isset($detailArray[0]['CURRENT_CITY'])) ? $detailArray[0]['CURRENT_CITY'] : '';
			$dataArray['Detail'] = $detailArray;
			$dataArray['courier_name'] = $courier_name;
			$Error1 = $doc->getElementsByTagName("Error");
			$Error = isset($Error1->item(0)->nodeValue) ? $Error1->item(0)->nodeValue : '';
			$dataArray['Message'] = $Error;

			if ($Error == '') {
				$StatusType = $doc->getElementsByTagName("StatusType");
				$StatusType1 = isset($StatusType->item(0)->nodeValue) ? $StatusType->item(0)->nodeValue : '';
				if ($StatusType1 == 'NF') {
					$dataArray['Message'] = "Incorrect Waybill number or No Information";
				}
			}
            return ["status"=>true, "data"=>$dataArray];
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>false, "error"=>$e->getMessage()];
        }
    }
}
