<?php
if (!defined('ABSPATH')) exit;

class W5E_API
{
    private $base;

    public function __construct()
    {
        $env = get_option('w5e_env', 'staging');
        $this->base = rtrim($this->get_base_for_env($env), '/');
    }

    private function http($method, $path, $args = [])
    {
        $url  = $this->base . $path;

        $defHeaders = [
            'Accept'       => 'application/json',
            'Content-Type' => 'application/json',
        ];

        $wpArgs = [
            'method'  => strtoupper($method),
            'timeout' => 20,
            'headers' => isset($args['headers']) ? array_merge($defHeaders, $args['headers']) : $defHeaders,
        ];

        if (!empty($args['query']) && is_array($args['query'])) {
            $qs = http_build_query($args['query']);
            $url .= (strpos($url, '?') === false ? '?' : '&') . $qs;
        }

        if (array_key_exists('body', $args)) {
            $wpArgs['body'] = is_string($args['body']) ? $args['body'] : wp_json_encode($args['body']);
        }

        $res  = wp_remote_request($url, $wpArgs);
        if (is_wp_error($res)) {
            throw new Exception($res->get_error_message());
        }
        $code = wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);
        $data = json_decode($body, true);

        return ['code' => $code, 'data' => $data, 'raw' => $body, 'url' => $url];
    }

    private function ok($code)
    {
        return $code >= 200 && $code < 300;
    }

    private function get_base_for_env($env)
    {
        $env = strtolower($env);
        if ($env === 'production') {
            $prod = trim((string)get_option('w5e_orders_base_url_production', ''));
            return $prod !== '' ? $prod : 'https://api.lastmile.rs';
        }
        $staging = trim((string)get_option('w5e_orders_base_url_staging', ''));
        if ($staging !== '') {
            return $staging;
        }
        return 'https://api-staging.5element.concordsofttest.com';
    }

    public function get_company_couriers_by_credentials($fiveEId, $apiKey)
    {
        $cached = get_option('w5e_couriers_endpoint');
        if (is_array($cached) && !empty($cached['method']) && !empty($cached['path']) && !empty($cached['mode'])) {
            $tryOne = $this->try_pattern($cached, $fiveEId, $apiKey);
            if ($this->ok($tryOne['code']) && is_array($tryOne['data'])) {
                return $tryOne['data'];
            }
        }

        $candidates = [
            [
                'method'    => 'GET',
                'path'      => '/company-courier-lists/get-by-credentials/%s',
                'mode'      => 'path',
                'paramCase' => 'lower',
                'headers'   => ['X-Auth-Key' => 'apiKey'],
                'pathArgs'  => ['fiveEId'],
            ],
            [
                'method'    => 'GET',
                'path'      => '/company-courier-lists/get-by-credentials/%s',
                'mode'      => 'path',
                'paramCase' => 'lower',
                'headers'   => ['X-Auth-Key' => 'fiveEId'],
                'pathArgs'  => ['apiKey'],
            ],

            ['method' => 'GET', 'path' => '/api/woocommerce/company-courier-list/by-credentials',      'mode' => 'query', 'paramCase' => 'lower'],
            ['method' => 'GET', 'path' => '/api/woocommerce/company-courier-list/by-credentials',      'mode' => 'query', 'paramCase' => 'pascal'],
            ['method' => 'GET', 'path' => '/api/woocommerce/company-courier-list/get-by-credentials',  'mode' => 'query', 'paramCase' => 'lower'],
            ['method' => 'GET', 'path' => '/api/woocommerce/company-courier-list',                     'mode' => 'query', 'paramCase' => 'lower'],
            ['method' => 'GET', 'path' => '/api/WooCommerce/company-courier-list/by-credentials',      'mode' => 'query', 'paramCase' => 'lower'],
            ['method' => 'GET', 'path' => '/api/woocommerce/company-courier-list/by-credentials/%s/%s', 'mode' => 'path',  'paramCase' => 'lower'],
            ['method' => 'POST', 'path' => '/api/woocommerce/company-courier-list/by-credentials',      'mode' => 'body',  'paramCase' => 'pascal'],
            ['method' => 'POST', 'path' => '/api/woocommerce/company-courier-list/by-credentials',      'mode' => 'body',  'paramCase' => 'lower'],
        ];

        $lastErr = null;

        foreach ($candidates as $pat) {
            try {
                $res = $this->try_pattern($pat, $fiveEId, $apiKey);
                if ($this->ok($res['code']) && is_array($res['data'])) {
                    update_option('w5e_couriers_endpoint', $pat, false);
                    return $res['data'];
                }
                if (in_array($res['code'], [401, 403], true)) {
                    throw new Exception(sprintf(__('Unauthorized (401/403) — verify the 5E ID and API Key. URL: %s', 'woo-5e'), $res['url']));
                }
                $lastErr = 'HTTP ' . $res['code'] . ' @ ' . $res['url'] . ' (body: ' . $res['raw'] . ')';
            } catch (Exception $e) {
                $lastErr = $e->getMessage();
            }
        }

        throw new Exception($lastErr ?: __('Could not fetch couriers: no endpoint accepted the request.', 'woo-5e'));
    }

    public function get_warehouses_by_client_credentials($fiveEId, $apiKey)
    {
        if (!$fiveEId || !$apiKey) {
            throw new Exception(__('Missing 5E credentials (ID or API key).', 'woo-5e'));
        }

        $path = '/warehouses/get-by-client-credentials/' . rawurlencode((string)$fiveEId);

        $res = $this->http('GET', $path, [
            'headers' => [
                'X-Auth-Key' => $apiKey,
            ],
        ]);

        if ($this->ok($res['code']) && is_array($res['data'])) {
            return $res['data'];
        }

        if (in_array($res['code'], [401, 403], true)) {
            throw new Exception(sprintf(__('Unauthorized (401/403) — verify the 5E ID and API Key. URL: %s', 'woo-5e'), $res['url']));
        }

        throw new Exception('HTTP ' . $res['code'] . ' @ ' . $res['url'] . ' (body: ' . $res['raw'] . ')');
    }

    private function try_pattern(array $pat, $fiveEId, $apiKey)
    {
        $method   = $pat['method'];
        $path     = $pat['path'];
        $mode     = $pat['mode'];
        $case     = $pat['paramCase'];

        if ($case === 'pascal') {
            $k1 = 'FiveEId';
            $k2 = 'ApiAuthKey';
        } else {
            $k1 = 'fiveEId';
            $k2 = 'apiAuthKey';
        }

        $headers = [];
        if (!empty($pat['headers']) && is_array($pat['headers'])) {
            foreach ($pat['headers'] as $headerName => $source) {
                if ($source === 'fiveEId') {
                    $headers[$headerName] = $fiveEId;
                } elseif ($source === 'apiKey') {
                    $headers[$headerName] = $apiKey;
                }
            }
        }

        $httpArgs = [];
        if ($headers) {
            $httpArgs['headers'] = $headers;
        }

        if ($mode === 'path') {
            $values = [];
            if (!empty($pat['pathArgs']) && is_array($pat['pathArgs'])) {
                foreach ($pat['pathArgs'] as $arg) {
                    if ($arg === 'fiveEId') {
                        $values[] = rawurlencode($fiveEId);
                    } elseif ($arg === 'apiKey') {
                        $values[] = rawurlencode($apiKey);
                    } else {
                        $values[] = rawurlencode((string)$arg);
                    }
                }
            } else {
                $values = [rawurlencode($fiveEId), rawurlencode($apiKey)];
            }
            $path = vsprintf($path, $values);
            return $this->http($method, $path, $httpArgs);
        }

        if ($mode === 'query') {
            $httpArgs['query'] = [$k1 => $fiveEId, $k2 => $apiKey];
            return $this->http($method, $path, $httpArgs);
        }

        $httpArgs['body'] = [$k1 => $fiveEId, $k2 => $apiKey];
        return $this->http($method, $path, $httpArgs);
    }

    public function process_order($clientId, $apiKey, array $payload, $env = 'staging')
    {
        if (!$clientId || !$apiKey) {
            throw new Exception(__('Missing Client ID or API key for order processing.', 'woo-5e'));
        }

        $base = $this->get_orders_base_url($env);
        $url  = trailingslashit($base) . 'orders/process/' . rawurlencode($clientId);

        $args = [
            'method'  => 'POST',
            'timeout' => 25,
            'headers' => [
                'Accept'       => 'application/json',
                'Content-Type' => 'application/json',
                'X-Auth-Key'   => $apiKey,
            ],
            'body'    => wp_json_encode($payload),
        ];

        $res = wp_remote_request($url, $args);
        if (is_wp_error($res)) {
            throw new Exception($res->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);
        if ($code < 200 || $code >= 300) {
            $message = $body ? $body : 'HTTP ' . $code;
            throw new Exception(sprintf(__('Order export failed (%1$s): %2$s', 'woo-5e'), $code, $message));
        }

        $data = json_decode($body, true);
        return is_array($data) ? $data : ['raw' => $body];
    }

    /**
     * Update order status in 5E (Woo endpoint with clientId in path and orderIds array).
     *
     * @param string $clientId
     * @param string $apiKey
     * @param array  $orderIds
     * @param string $status
     * @param string $reason
     * @param string $env
     * @return array
     * @throws Exception
     */
    public function update_order_status($clientId, $apiKey, array $orderIds, $status, $reason = '', $env = 'staging')
    {
        // Preserve alphanumeric IDs; don't cast to int.
        $orderIds = array_values(array_filter(array_map(function ($id) {
            $id = is_scalar($id) ? trim((string)$id) : '';
            return $id !== '' ? $id : null;
        }, $orderIds)));

        if (empty($orderIds)) {
            throw new Exception(__('Missing order IDs for status update.', 'woo-5e'));
        }
        if (!$clientId || !$apiKey) {
            throw new Exception(__('Missing Client ID or API key for status update.', 'woo-5e'));
        }

        $base = $this->get_orders_base_url($env);
        $url  = trailingslashit($base) . 'orders/update-statuses-from-woo/' . rawurlencode((string)$clientId);

        $payload = [
            'orderStatus' => (string)$status,
            'orderIds'    => $orderIds,
            'reason'      => (string)$reason,
        ];

        $args = [
            'method'  => 'PUT',
            'timeout' => 20,
            'headers' => [
                'Accept'       => 'application/json',
                'Content-Type' => 'application/json',
                'X-Auth-Key'   => $apiKey,
            ],
            'body'    => wp_json_encode($payload),
        ];

        $res = wp_remote_request($url, $args);
        if (is_wp_error($res)) {
            throw new Exception($res->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);
        if ($code < 200 || $code >= 300) {
            $message = $body ? $body : 'HTTP ' . $code;
            throw new Exception(sprintf(__('Status update failed (%1$s): %2$s', 'woo-5e'), $code, $message));
        }

        $data = json_decode($body, true);
        return is_array($data) ? $data : ['raw' => $body];
    }

    private function get_orders_base_url($env)
    {
        $env = strtolower($env);
        if ($env === 'production') {
            $prod = trim((string)get_option('w5e_orders_base_url_production', ''));
            return $prod !== '' ? trailingslashit($prod) : 'https://api.lastmile.rs/';
        }

        $staging = trim((string)get_option('w5e_orders_base_url_staging', ''));
        if ($staging !== '') {
            return trailingslashit($staging);
        }

        return 'https://api-staging.5element.concordsofttest.com/';
    }
}
