<?php

defined("ABSPATH") || exit();

/**
 * Wildrobot_Logistra_Ajax class.
 */
class Wildrobot_Logistra_Cargonizer
{
    // helthjem_standard is just here for testing
    private static $helthjem_products = [
        "helthjem_ekspress_standard_mypack",
        "helthjem_hentepakke",
        "helthjem_standard_mypack",
        "helthjem_mypack",
        "helthjem_hentepakke_return",
    ];

    public static function init()
    {
        // nothing here yet
    }

    public static function get_endpoint($endpoint, $cargonizer_provider)
    {
        if ($cargonizer_provider == "PROFRAKT") {
            $profrakt_endspoints = [
                "service_partners" => "/pickup_points.xml",
                "consignment_cost" => "/cost_estimation.xml",
                "printers" => "/cloudprinters.xml",
            ];
            return $profrakt_endspoints[$endpoint];
        } else {
            $logistra_endpoints = [
                "service_partners" => "/service_partners.xml",
                "consignment_cost" => "/consignment_costs.xml",
                "printers" => "/printers.xml",
            ];
            return $logistra_endpoints[$endpoint];
        }
    }

    public static function get_headers(
        $api_key,
        $sender_id,
        $cargonizer_provider,
    ) {
        if ($cargonizer_provider == "PROFRAKT") {
            return [
                "X-Profrakt-Key" => $api_key,
                "X-Profrakt-Sender" => $sender_id,
            ];
        }
        // default to Logistra
        return [
            "X-Cargonizer-Key" => $api_key,
            "X-Cargonizer-Sender" => $sender_id,
        ];
    }
    public static function get_url($cargonizer_provider)
    {
        if ($cargonizer_provider == "PROFRAKT") {
            return get_option(
                "wildrobot_logistra_cargonizer_backend_url_profrakt",
            );
        }
        // default to Logistra
        return get_option("wildrobot_logistra_cargonizer_backend_url");
    }

    public static function get_printers()
    {
        $cargonizer_provider = get_option(
            "wildrobot_logistra_cargonizer_provider",
            "LOGISTRA",
        );
        $sendersCommaDelimted = get_option("wildrobot_logistra_senders", false);
        $senders = explode(",", $sendersCommaDelimted);
        $printers = [
            [
                "name" => "Manuell printing",
                "id" => "9999999",
            ],
            [
                "name" => "Ingen printer (fjerner valgt printer)",
                "id" => "",
            ],
        ];
        foreach ($senders as $senderId) {
            $response = wp_remote_get(
                self::get_url($cargonizer_provider) .
                    self::get_endpoint("printers", $cargonizer_provider),
                [
                    "headers" => self::get_headers(
                        get_option("wildrobot_logistra_cargonizer_apikey", ""),
                        $senderId,
                        $cargonizer_provider,
                    ),
                ],
            );
            $repsponseBody = wp_remote_retrieve_body($response);
            $xml = simplexml_load_string($repsponseBody);
            $json = json_encode($xml);
            $array = json_decode($json, true);
            // If we get an empty array we must handle this like this
            if (!array_key_exists("printer", $array)) {
                continue;
            }
            if (array_key_exists("id", $array["printer"])) {
                $printers = array_merge($printers, [0 => $array["printer"]]);
            } else {
                $printers = array_merge($printers, $array["printer"]);
            }
        }
        foreach ($printers as $key => $printer) {
            if (is_array($printer["id"])) {
                $printers[$key]["id"] = $printer["id"][0];
            }
        }
        $unique_printers = self::better_array_unique($printers);
        return $unique_printers;
    }

    // Cache version - increment to invalidate all service partner transients
    private static $service_partner_cache_version = "v4";

    public static function get_service_partners(
        $country,
        $postcode,
        $carrier,
        $wr_id,
        $address = null,
        $city = null,
        $service_partner_type = null,
    ) {
        $cache_version = self::$service_partner_cache_version;
        if (empty($address)) {
            $sanitized_type = $service_partner_type ?? "all";
            $transient_name =
                $country .
                "-" .
                $postcode .
                "-" .
                $wr_id .
                "-" .
                $sanitized_type .
                $city .
                "-wildrobot-" .
                $cache_version;
        } else {
            $formatted_address = preg_replace("/[^a-zA-Z0-9]+/", "", $address);
            $sanitized_type = $service_partner_type ?? "all";
            $transient_name =
                $country .
                "-" .
                $postcode .
                "-" .
                $wr_id .
                $city .
                "-" .
                $formatted_address .
                "-" .
                $sanitized_type .
                "-wildrobot-" .
                $cache_version;
        }
        $transient = get_transient($transient_name);
        if (
            empty($transient) ||
            (array_key_exists("service_partners", $transient) &&
                empty($transient["service_partners"]))
        ) {
            $logger = new WC_Logger();
            $context = ["source" => "wildrobot-logistra-service-partners"];
            $api_key = get_option("wildrobot_logistra_cargonizer_apikey", "");
            $sender_id = get_option("wildrobot_logistra_sender_id", "");
            $cargonizer_provider = get_option(
                "wildrobot_logistra_cargonizer_provider",
                "LOGISTRA",
            );

            $params = [
                "country" => esc_html($country),
                "postcode" => esc_html($postcode),
                "carrier" => esc_html(strtolower($carrier)),
            ];
            if (!empty($city)) {
                $params = array_merge($params, [
                    "city" => esc_html($city),
                ]);
            }
            if (!empty($address)) {
                $params = array_merge($params, [
                    "address" => esc_html($address),
                ]);
            }
            if (
                $cargonizer_provider === "PROFRAKT" &&
                get_option("wildrobot_logistra_filter_out_pakkeautomat") ===
                    "yes"
            ) {
                $params = array_merge($params, [
                    "type" => "manned",
                ]);
            }
            if (!empty($service_partner_type)) {
                $params = array_merge($params, [
                    "type" => esc_html($service_partner_type),
                ]);
            }

            if (!empty($wr_id)) {
                $wr_data = Wildrobot_Logistra_DB::get_transport_product_and_agreement_id(
                    $wr_id,
                );
                if ($wr_data["product"] === "postnord_mypack_small") {
                    $params = array_merge($params, [
                        "product" => "postnord_mypack_small",
                    ]);
                } elseif (
                    in_array($wr_data["product"], self::$helthjem_products)
                ) {
                    // Handle helthjem spesific
                    $helthjem_customer_consignee_number = get_transient(
                        "wildrobot_logistra_helthjem_customer_consignee_number",
                    );
                    if (empty($helthjem_customer_consignee_number)) {
                        $transport_agreement = Wildrobot_Logistra_DB::get_transport_agreement_for_logistra_identifier(
                            $wr_id,
                        );
                        if (empty($transport_agreement)) {
                            throw new Exception(
                                "Helthjem transport avtale ikke funnet, prøv å oppdatere dine transportavtaler.",
                            );
                        }
                        $helthjem_customer_consignee_number =
                            $transport_agreement["ta_number"];
                        set_transient(
                            "wildrobot_logistra_helthjem_customer_consignee_number",
                            $helthjem_customer_consignee_number,
                            20 * HOUR_IN_SECONDS,
                        );
                    }
                    if (empty($helthjem_customer_consignee_number)) {
                        throw new Exception(
                            "Fra 1 September krever Helthjem kunde nummer ved utlveringssted forespørsel. Klarte ikke å finne ditt HeltHjem kundenummer.",
                        );
                    }
                    // TODO - Check if this helthjem product requires service partner
                    $params = array_merge($params, [
                        "product" => esc_html($wr_data["product"]),
                        "shop_id" => esc_html(
                            $helthjem_customer_consignee_number,
                        ),
                    ]);
                } elseif (!empty($wr_data["product"])) {
                    $params = array_merge($params, [
                        "product" => $wr_data["product"],
                    ]);
                }
            }
            $query = add_query_arg(
                $params,
                self::get_url($cargonizer_provider) .
                    self::get_endpoint(
                        "service_partners",
                        $cargonizer_provider,
                    ),
            );
            $logger->info("URL => " . $query, $context);
            $response = wp_remote_get($query, [
                "headers" => self::get_headers(
                    $api_key,
                    $sender_id,
                    $cargonizer_provider,
                ),
            ]);

            $repsponseBody = wp_remote_retrieve_body($response);
            $xml = simplexml_load_string($repsponseBody);
            $json = json_encode($xml);
            $data = json_decode($json, true);

            if (
                is_array($data) &&
                array_key_exists("service-partners", $data) &&
                array_key_exists("service-partner", $data["service-partners"])
            ) {
                if (
                    array_key_exists(
                        "number",
                        $data["service-partners"]["service-partner"],
                    )
                ) {
                    $data["service_partners"] = [
                        0 => $data["service-partners"]["service-partner"],
                    ];
                } else {
                    $data["service_partners"] =
                        $data["service-partners"]["service-partner"];
                }
            }

            if (array_key_exists("errors", $data) && !empty($data["errors"])) {
                $logger->error(wc_print_r($data["errors"], true), $context);
            }
            unset($data["service-partners"]);

            if (!empty($data["service_partners"])) {
                if (
                    get_option("wildrobot_logistra_filter_out_pakkeautomat") ===
                        "yes" &&
                    $service_partner_type === null
                ) {
                    $data["service_partners"] = array_values(
                        array_filter($data["service_partners"], function (
                            $service_partner,
                        ) {
                            foreach (["Pakkeautomat", "Pakkeboks"] as $name) {
                                if (
                                    str_contains(
                                        $service_partner["name"],
                                        $name,
                                    )
                                ) {
                                    return false;
                                }
                            }
                            return true;
                        }),
                    );

                    // After filtering, check if list became empty - if so, add fallback
                    if (empty($data["service_partners"])) {
                        if (
                            in_array($carrier, ["tollpost_globe", "postnord"])
                        ) {
                            $logger->info(
                                "All service partners filtered out (Pakkeautomat/Pakkeboks), using fallback",
                                $context,
                            );
                            $data["service_partners"] = [
                                0 => [
                                    "customer-number" => esc_html(
                                        "00098287279",
                                    ),
                                    "number" => esc_html("0413898"),
                                    "name" => esc_html("Postens Godssenter"),
                                    "country" => esc_html("NO"),
                                    "postcode" => esc_html("0068"),
                                    "city" => esc_html("Oslo"),
                                    "is_fallback" => true,
                                ],
                            ];
                        } else {
                            throw new Exception(
                                "Fant ingen utleveringssteder etter filtrering.",
                            );
                        }
                    }
                }
                $logger->info(
                    "Number of service partners: " .
                        count($data["service_partners"]),
                    $context,
                );
                set_transient(
                    $transient_name,
                    apply_filters(
                        "wildrobot_logistra_avaiable_service_partners",
                        $data,
                    ),
                    7 * DAY_IN_SECONDS,
                );
            } else {
                // Should only fallback for Postnord
                if (in_array($carrier, ["tollpost_globe", "postnord"])) {
                    $data["service_partners"] = [
                        0 => [
                            "customer-number" => esc_html("00098287279"),
                            "number" => esc_html("0413898"),
                            "name" => esc_html("Postens Godssenter"),
                            "country" => esc_html("NO"),
                            "postcode" => esc_html("0068"),
                            "city" => esc_html("Oslo"),
                            "is_fallback" => true,
                        ],
                    ];
                } else {
                    throw new Exception("Fant ingen utleveringssteder.");
                }
            }
        } else {
            $data = $transient;
        }
        return apply_filters(
            "wildrobot_logistra_service_partner_request",
            $data,
        );
    }

    public static function estimate_consignment($consignment_xml)
    {
        $hash = md5(wp_json_encode($consignment_xml));
        $transient = get_transient(
            "wildrobot-logistra-freightcost-estimate-" . $hash,
        );
        if (
            !empty($transient) &&
            !Wildrobot_Logistra_Utils::is_test_enviroment()
        ) {
            return $transient;
        }
        $cargonizer_provider = get_option(
            "wildrobot_logistra_cargonizer_provider",
            "LOGISTRA",
        );
        $sender_id = apply_filters(
            "wildrobot_logistra_override_estimate_sender_id",
            get_option("wildrobot_logistra_sender_id", ""),
        );
        $response = wp_remote_post(
            self::get_url($cargonizer_provider) .
                self::get_endpoint("consignment_cost", $cargonizer_provider),
            [
                "headers" => self::get_headers(
                    get_option("wildrobot_logistra_cargonizer_apikey", ""),
                    $sender_id,
                    $cargonizer_provider,
                ),
                "body" => $consignment_xml,
            ],
        );

        if (is_wp_error($response)) {
            throw new Exception($response->get_error_message());
        }
        $responseBody = wp_remote_retrieve_body($response);
        $xml = simplexml_load_string($responseBody);
        if ($xml === false) {
            throw new Exception(
                "Invalid response from Logistra API (could not parse XML): " .
                    substr($responseBody, 0, 200),
            );
        }
        $json = json_encode($xml);
        $data = json_decode($json, true);
        if (!is_array($data)) {
            throw new Exception(
                "Invalid response from Logistra API (could not decode response)",
            );
        }
        if (array_key_exists("error", $data)) {
            throw new Exception($data["error"]);
        }
        if (array_key_exists("consignment", $data)) {
            if (array_key_exists("errors", $data["consignment"])) {
                if (array_key_exists("error", $data["consignment"]["errors"])) {
                    if (is_array($data["consignment"]["errors"]["error"])) {
                        throw new Exception(
                            implode(
                                ", ",
                                $data["consignment"]["errors"]["error"],
                            ),
                        );
                    } else {
                        throw new Exception(
                            $data["consignment"]["errors"]["error"],
                        );
                    }
                }
            }
        }
        set_transient(
            "wildrobot-logistra-freightcost-estimate-" . $hash,
            $data,
            7 * DAY_IN_SECONDS,
        );
        return $data;
    }

    public static function print_consignment($printer_id, $consignment_id)
    {
        $cargonizer_provider = get_option(
            "wildrobot_logistra_cargonizer_provider",
            "LOGISTRA",
        );
        $api_key = get_option("wildrobot_logistra_cargonizer_apikey", "");
        $sender_id = get_option("wildrobot_logistra_sender_id", "");

        $url =
            self::get_url($cargonizer_provider) . "/consignments/label_direct";
        $params = [
            "printer_id" => $printer_id,
            "consignment_ids[]" => $consignment_id,
        ];

        $query = add_query_arg($params, $url);

        $response = wp_remote_post($query, [
            "method" => "POST",
            "headers" => self::get_headers(
                $api_key,
                $sender_id,
                $cargonizer_provider,
            ),
        ]);

        if (is_wp_error($response)) {
            throw new Exception(
                sprintf(
                    /* translators: %s: HTTP error message */
                    __(
                        "Kunne ikke kontakte fraktleverandøren: %s",
                        "wildrobot-logistra",
                    ),
                    $response->get_error_message(),
                ),
            );
        }

        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);

        // Cargonizer returns 201 Created on a successful print job, so a strict
        // "=== 200" check wrongly treats a successful print as an error. Accept
        // any 2xx, matching how the rest of the codebase handles API responses.
        if (!in_array($response_code, [200, 201, 202], true)) {
            error_log(
                "Error printing consignment. Response code: " .
                    $response_code .
                    ". Response body: " .
                    $response_body,
            );
            throw new Exception(
                self::get_print_error_message($response_code, $response_body),
            );
        }

        return $response_body;
    }

    /**
     * Build a human-readable error message from a failed label_direct response.
     * Cargonizer returns its errors as XML, e.g. <errors><error>..</error></errors>.
     */
    private static function get_print_error_message(
        $response_code,
        $response_body,
    ) {
        $message = "";

        if (!empty($response_body)) {
            $previous = libxml_use_internal_errors(true);
            $xml = simplexml_load_string($response_body);
            libxml_clear_errors();
            libxml_use_internal_errors($previous);

            if ($xml !== false) {
                if (isset($xml->errors->error)) {
                    $message = trim((string) $xml->errors->error);
                } elseif (isset($xml->error)) {
                    $message = trim((string) $xml->error);
                } elseif (trim((string) $xml) !== "") {
                    $message = trim((string) $xml);
                }
            }
        }

        if ($message === "") {
            switch ((int) $response_code) {
                case 401:
                case 403:
                    $message = __(
                        "Ugyldig API-nøkkel eller manglende tilgang hos fraktleverandøren.",
                        "wildrobot-logistra",
                    );
                    break;
                case 404:
                    $message = __(
                        "Fant ikke sendingen eller skriveren hos fraktleverandøren.",
                        "wildrobot-logistra",
                    );
                    break;
                case 0:
                    $message = __(
                        "Ingen respons fra fraktleverandøren.",
                        "wildrobot-logistra",
                    );
                    break;
                default:
                    $message = __(
                        "Ukjent feil hos fraktleverandøren.",
                        "wildrobot-logistra",
                    );
            }
        }

        return sprintf(
            /* translators: 1: error detail, 2: HTTP status code */
            __(
                "Kunne ikke skrive ut etiketten: %1\$s (kode %2\$s)",
                "wildrobot-logistra",
            ),
            $message,
            $response_code,
        );
    }

    function wildrobot_logistra_print_consignment_ajax()
    {
        // This link lives long-term in the order notes, so a short-lived nonce
        // (it expires after ~12-24h) would break it with a cryptic "-1". The
        // action is capability-gated and idempotent (it only reprints a label),
        // so we rely on the capability check as the security gate instead.
        if (!current_user_can("edit_shop_orders")) {
            self::render_print_result_page(
                __(
                    "Du har ikke tilgang til å skrive ut fraktetiketter.",
                    "wildrobot-logistra",
                ),
                false,
            );
        }

        $printer_id = isset($_GET["printer_id"])
            ? sanitize_text_field(wp_unslash($_GET["printer_id"]))
            : "";
        $consignment_id = isset($_GET["consignment_id"])
            ? sanitize_text_field(wp_unslash($_GET["consignment_id"]))
            : "";

        if (empty($printer_id) || empty($consignment_id)) {
            self::render_print_result_page(
                __(
                    "Lenken mangler nødvendig informasjon (skriver eller sending). Last ordresiden på nytt og prøv igjen.",
                    "wildrobot-logistra",
                ),
                false,
            );
        }

        try {
            Wildrobot_Logistra_Cargonizer::print_consignment(
                $printer_id,
                $consignment_id,
            );
        } catch (\Throwable $e) {
            error_log("Wildrobot Logistra reprint failed: " . $e->getMessage());
            self::render_print_result_page($e->getMessage(), false);
        }

        self::render_print_result_page(
            __("Etiketten ble sendt til skriveren.", "wildrobot-logistra"),
            true,
        );
    }

    /**
     * Render a small, human-readable result page after a reprint attempt.
     *
     * The reprint link is opened as a normal page navigation (not XHR), so we
     * output a self-contained HTML page instead of WordPress' raw "-1"/"0"/JSON.
     */
    private static function render_print_result_page($message, $success)
    {
        $title = $success
            ? __("Etikett skrevet ut", "wildrobot-logistra")
            : __("Utskrift mislyktes", "wildrobot-logistra");

        $icon = $success ? "✓" : "✕";
        $accent = $success ? "#2e7d32" : "#b32d2e";

        $referer = wp_get_referer();
        $back_url = $referer
            ? $referer
            : admin_url("edit.php?post_type=shop_order");

        if (!headers_sent()) {
            header("Content-Type: text/html; charset=utf-8");
        }

        printf(
            '<!DOCTYPE html><html lang="%1$s"><head><meta charset="utf-8">' .
                '<meta name="viewport" content="width=device-width,initial-scale=1">' .
                "<title>%2\$s</title></head>" .
                '<body style="font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,Helvetica,Arial,sans-serif;background:#f0f0f1;margin:0;">' .
                '<div style="max-width:480px;margin:80px auto;background:#fff;border:1px solid #dcdcde;border-radius:8px;padding:32px;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.06);">' .
                '<div style="font-size:56px;line-height:1;color:%3$s;margin-bottom:16px;">%4$s</div>' .
                '<h1 style="font-size:20px;margin:0 0 8px;color:#1d2327;">%5$s</h1>' .
                '<p style="font-size:15px;color:#3c434a;margin:0 0 24px;">%6$s</p>' .
                '<a href="%7$s" style="display:inline-block;background:#2271b1;color:#fff;text-decoration:none;padding:10px 20px;border-radius:4px;font-size:14px;">%8$s</a>' .
                "</div></body></html>",
            esc_attr(get_bloginfo("language")),
            esc_html($title),
            esc_attr($accent),
            esc_html($icon),
            esc_html($title),
            esc_html($message),
            esc_url($back_url),
            esc_html__("Tilbake til ordren", "wildrobot-logistra"),
        );
        exit();
    }

    private static function better_array_unique($array, $keep_key_assoc = false)
    {
        $duplicate_keys = [];
        $tmp = [];

        foreach ($array as $key => $val) {
            // convert objects to arrays, in_array() does not support objects
            if (is_object($val)) {
                $val = (array) $val;
            }

            if (!in_array($val, $tmp)) {
                $tmp[] = $val;
            } else {
                $duplicate_keys[] = $key;
            }
        }

        foreach ($duplicate_keys as $key) {
            unset($array[$key]);
        }

        return $keep_key_assoc ? $array : array_values($array);
    }
}

Wildrobot_Logistra_Cargonizer::init();
