<?php

class Tracking_UPS {

    /**
     *
     * @param string $code
     * @return \TrackingInfo
     * @throws TrackingException
     */
    public static function track(string $code, string $carrier = ""): TrackingInfo {
        $barcode = new TrackingBarcode($code);

        try {
            $trackrequest = [
                "Security" => [
                    "UsernameToken" => [
                        "Username" => env("ups_user_account"),
                        "Password" => env("ups_password")
                    ],
                    "UPSServiceAccessToken" => [
                        "AccessLicenseNumber" => env("ups_access_key")
                    ]
                ],
                "TrackRequest" => [
                    "Request" => [
                        "RequestAction" => "Track",
                        "RequestOption" => "activity"
                    ],
                    "InquiryNumber" => $code
                ]
            ];
            $headers = [
                'Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept',
                'Access-Control-Allow-Methods: POST',
                'Access-Control-Allow-Origin: *',
                'Content-Type: application/json'
            ];

            $ch = curl_init();
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
            curl_setopt($ch, CURLOPT_TIMEOUT, 45);
            //curl_setopt($ch, CURLOPT_URL, "https://wwwcie.ups.com/rest/Track"); // TEST
            curl_setopt($ch, CURLOPT_URL, "https://onlinetools.ups.com/rest/Track"); // PROD
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($trackrequest));
            $response = curl_exec($ch);

            //header("Content-Type: application/json");
            //echo json_encode(json_decode($response), JSON_PRETTY_PRINT);
            //exit();

            // CHECK TO SEE IF WE GOT AN ERROR
            // IF SO, FORMAT IT LIKE THIS   ::28::Operation timed out afterseconds
            if ((curl_errno($ch)) && (curl_errno($ch) != 0)) {
                throw new TrackingException(curl_error($ch));
            }

            $result = json_decode($response, true);

            if (!empty($result["response"])) {
                // Should have trackResponse instead...
                if (!empty($result["response"]["errors"][0]["message"])) {
                    throw new TrackingException($result["response"]["errors"][0]["message"]);
                }
                throw new TrackingException("The UPS tracking system is having problems. Try again later.");
            }
            if (!empty($result["Fault"])) {
                // Should have trackResponse instead...
                if (!empty($result["Fault"]["detail"]["Errors"]["ErrorDetail"]["PrimaryErrorCode"]["Description"])) {
                    throw new TrackingException($result["Fault"]["detail"]["Errors"]["ErrorDetail"]["PrimaryErrorCode"]["Description"]);
                }
                throw new TrackingException("The UPS tracking system is having problems. Try again later.");
            }

            if (!empty($result["TrackResponse"]["Shipment"]) && !empty($result["TrackResponse"]["Shipment"])) {
                $trackinfo = $result["TrackResponse"]["Shipment"];
            } else {
                throw new TrackingException("No tracking details found.");
            }
            //exitWithJson($trackinfo);
        } catch (TrackingException $ex) {
            throw $ex;
        } catch (Exception $ex) {
            throw new TrackingException("There was a server problem. This code cannot be tracked right now.");
        }

        $info = new TrackingInfo();
        if (!empty($trackinfo["Package"]["TrackingNumber"])) {
            $info->setCode($trackinfo["Package"]["TrackingNumber"]);
        } else if (is_array($trackinfo["Package"])) {
            // More than one linked package in one shipment, get rid of the extra and make the
            // schema match what we expect
            $package = [];
            for ($i = 0; $i < count($trackinfo["Package"]); $i++) {
                if ($trackinfo["Package"][$i]["TrackingNumber"] == $code) {
                    $info->setCode($trackinfo["Package"][$i]["TrackingNumber"]);
                    $package = $trackinfo["Package"][$i];
                    $trackinfo["Package"] = $package;
                    break;
                }
            }
        }

        $info->setCarrier("ups");
        $info->setService(new Service($trackinfo["Service"]["Code"], $trackinfo["Service"]["Description"]));
        $info->setCarrierAttributionText(CarrierAssets::getAttribution(Carriers::getCarrierCode($info->getCarrier())));
        $info->setCarrierLogo(CarrierAssets::getLogo(Carriers::getCarrierCode($info->getCarrier())));

        if (count($trackinfo["Package"]["Activity"]) > 0) {
            // If there's only one entry, it might not be an array
            if (isset($trackinfo["Package"]["Activity"][0])) {
                $current = $trackinfo["Package"]["Activity"][0];
            } else {
                $current = $trackinfo["Package"]["Activity"];
            }
            $current_status = new TrackingEntry(
                    TrackingStatus::UPSEventTypeToStatus($current["Status"]["Type"]),
                    $current["Status"]["Description"] ?? "Unknown",
                    DateTime::createFromFormat('Ymd His', "$current[Date] $current[Time]")->format("c")
            );

            $current_location = new Location();
            $current_location->city = $current["ActivityLocation"]["Address"]["City"] ?? "";
            $current_location->state = $current["ActivityLocation"]["Address"]["StateProvinceCode"] ?? "";
            $current_location->zip = $current["ActivityLocation"]["Address"]["PostalCode"] ?? "";
            $current_location->country = $current["ActivityLocation"]["Address"]["CountryCode"] ?? "";
            $current_status->setLocation($current_location);

            $info->setCurrentStatus($current_status);
        }

        foreach ($trackinfo["ShipmentAddress"] as $address) {
            switch ($address["Type"]["Code"]) {
                case "02": // ShipTo Address
                    $to = new Location();
                    $to->city = $address["Address"]["City"] ?? "";
                    $to->state = $address["Address"]["StateProvinceCode"] ?? "";
                    $to->zip = $address["Address"]["PostalCode"] ?? "";
                    $to->country = $address["Address"]["CountryCode"] ?? "";
                    $info->setTo($to);
                    break;
            }
        }
//
//        $from = new Location();
//        $from->city = (string) $trackinfo->OriginCity ?? "";
//        $from->state = (string) $trackinfo->OriginState ?? "";
//        $from->zip = (string) $trackinfo->OriginZip ?? "";
//        $from->country = (string) $trackinfo->OriginCountryCode ?? "";
//
//        $info->setFrom($from);
        // Only one entry, so put it in itself so the loop works
        if (!isset($trackinfo["Package"]["Activity"][0])) {
            $trackinfo["Package"]["Activity"] = [
                $trackinfo["Package"]["Activity"]
            ];
        }
        foreach ($trackinfo["Package"]["Activity"] as $history) {
            $location = new Location();
            $location->city = $history["ActivityLocation"]["Address"]["City"] ?? "";
            $location->state = $history["ActivityLocation"]["Address"]["StateProvinceCode"] ?? "";
            $location->zip = $history["ActivityLocation"]["Address"]["PostalCode"] ?? "";
            $location->country = $history["ActivityLocation"]["Address"]["CountryCode"] ?? "";
            if (!empty($history["Date"]) && !empty($history["Time"])) {
                $datetimestring = DateTime::createFromFormat('Ymd His', "$history[Date] $history[Time]")->format("c");
            } else if (!empty($history["Date"])) {
                $datetimestring = DateTime::createFromFormat('Ymd His', "$history[Date]")->format("c");
            } else {
                $datetimestring = "";
            }
            $info->appendHistoryEntry(new TrackingEntry(
                            TrackingStatus::UPSEventTypeToStatus($history["Status"]["Type"]),
                            $history["Status"]["Description"] ?? "Unknown",
                            $datetimestring,
                            $location
            ));
        }

        return $info;
    }

}
