<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Http\SslCertificateResolver;

final class ShopproApiClient
{
    /**
     * @return array{ok:bool,http_code:int|null,message:string,items:array<int,array<string,mixed>>,total:int,page:int,per_page:int}
     */
    public function fetchOrders(
        string $baseUrl,
        string $apiKey,
        int $timeoutSeconds,
        int $page = 1,
        int $perPage = 100,
        ?string $fromDate = null
    ): array {
        $query = [
            'endpoint' => 'orders',
            'action' => 'list',
            'page' => max(1, $page),
            'per_page' => max(1, min(100, $perPage)),
            'sort' => 'updated_at',
            'sort_dir' => 'ASC',
        ];

        $dateFrom = trim((string) $fromDate);
        if ($dateFrom !== '') {
            $query['date_from'] = $dateFrom;
            $query['updated_from'] = $dateFrom;
        }

        $url = rtrim(trim($baseUrl), '/') . '/api.php?' . http_build_query($query);
        $response = $this->requestJson($url, $apiKey, $timeoutSeconds);
        if (($response['ok'] ?? false) !== true) {
            return [
                'ok' => false,
                'http_code' => $response['http_code'] ?? null,
                'message' => (string) ($response['message'] ?? 'Nie mozna pobrac listy zamowien z shopPRO.'),
                'items' => [],
                'total' => 0,
                'page' => max(1, $page),
                'per_page' => max(1, min(100, $perPage)),
            ];
        }

        $data = is_array($response['data'] ?? null) ? $response['data'] : [];
        $items = [];
        if (isset($data['items']) && is_array($data['items'])) {
            $items = $data['items'];
        } elseif (isset($data['orders']) && is_array($data['orders'])) {
            $items = $data['orders'];
        } elseif ($data !== [] && array_keys($data) === range(0, count($data) - 1)) {
            $items = $data;
        }

        return [
            'ok' => true,
            'http_code' => $response['http_code'] ?? null,
            'message' => '',
            'items' => array_values(array_filter($items, static fn (mixed $row): bool => is_array($row))),
            'total' => (int) ($data['total'] ?? count($items)),
            'page' => (int) ($data['page'] ?? max(1, $page)),
            'per_page' => (int) ($data['per_page'] ?? max(1, min(100, $perPage))),
        ];
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,order:array<string,mixed>|null}
     */
    public function fetchOrderById(string $baseUrl, string $apiKey, int $timeoutSeconds, string $orderId): array
    {
        $id = trim($orderId);
        if ($id === '') {
            return [
                'ok' => false,
                'http_code' => null,
                'message' => 'Niepoprawne ID zamowienia.',
                'order' => null,
            ];
        }

        $base = rtrim(trim($baseUrl), '/');
        $attemptUrls = [
            $base . '/api.php?' . http_build_query(['endpoint' => 'orders', 'action' => 'get', 'id' => $id]),
            $base . '/api.php?' . http_build_query(['endpoint' => 'orders', 'action' => 'details', 'id' => $id]),
        ];

        $lastMessage = 'Nie mozna pobrac szczegolow zamowienia z shopPRO.';
        $lastCode = null;

        foreach ($attemptUrls as $url) {
            $response = $this->requestJson($url, $apiKey, $timeoutSeconds);
            if (($response['ok'] ?? false) !== true) {
                $lastMessage = trim((string) ($response['message'] ?? $lastMessage));
                $lastCode = $response['http_code'] ?? null;
                continue;
            }

            $data = $response['data'] ?? null;
            if (!is_array($data)) {
                continue;
            }

            if (isset($data['order']) && is_array($data['order'])) {
                return [
                    'ok' => true,
                    'http_code' => $response['http_code'] ?? null,
                    'message' => '',
                    'order' => $data['order'],
                ];
            }

            return [
                'ok' => true,
                'http_code' => $response['http_code'] ?? null,
                'message' => '',
                'order' => $data,
            ];
        }

        return [
            'ok' => false,
            'http_code' => $lastCode,
            'message' => $lastMessage,
            'order' => null,
        ];
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,product:array<string,mixed>|null}
     */
    public function fetchProductById(string $baseUrl, string $apiKey, int $timeoutSeconds, int $productId): array
    {
        if ($productId <= 0) {
            return [
                'ok' => false,
                'http_code' => null,
                'message' => 'Niepoprawne ID produktu.',
                'product' => null,
            ];
        }

        $normalizedBaseUrl = rtrim(trim($baseUrl), '/');
        $query = http_build_query([
            'endpoint' => 'products',
            'action' => 'get',
            'id' => $productId,
        ]);
        $endpointUrl = $normalizedBaseUrl . '/api.php?' . $query;

        $response = $this->requestJson($endpointUrl, $apiKey, $timeoutSeconds);
        if (($response['ok'] ?? false) !== true) {
            return [
                'ok' => false,
                'http_code' => $response['http_code'] ?? null,
                'message' => (string) ($response['message'] ?? 'Nie mozna pobrac produktu z shopPRO.'),
                'product' => null,
            ];
        }

        $data = is_array($response['data'] ?? null) ? $response['data'] : null;
        if ($data === null) {
            return [
                'ok' => false,
                'http_code' => $response['http_code'] ?? null,
                'message' => 'shopPRO zwrocil pusty payload produktu.',
                'product' => null,
            ];
        }

        return [
            'ok' => true,
            'http_code' => $response['http_code'] ?? null,
            'message' => '',
            'product' => $data,
        ];
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,changed:bool}
     */
    public function updateOrderStatus(
        string $baseUrl,
        string $apiKey,
        int $timeoutSeconds,
        int $orderId,
        int $statusId,
        bool $sendEmail = false
    ): array {
        if ($orderId <= 0 || $statusId <= 0) {
            return [
                'ok' => false,
                'http_code' => null,
                'message' => 'Niepoprawne ID zamowienia lub statusu.',
                'changed' => false,
            ];
        }

        $url = rtrim(trim($baseUrl), '/') . '/api.php?' . http_build_query([
            'endpoint' => 'orders',
            'action' => 'change_status',
            'id' => $orderId,
        ]);

        $jsonBody = json_encode(['status_id' => $statusId, 'send_email' => $sendEmail], JSON_THROW_ON_ERROR);

        $response = $this->requestJsonPut($url, $apiKey, $timeoutSeconds, $jsonBody);
        if (($response['ok'] ?? false) !== true) {
            return [
                'ok' => false,
                'http_code' => $response['http_code'] ?? null,
                'message' => (string) ($response['message'] ?? 'Nie mozna zaktualizowac statusu zamowienia w shopPRO.'),
                'changed' => false,
            ];
        }

        $data = is_array($response['data'] ?? null) ? $response['data'] : [];

        return [
            'ok' => true,
            'http_code' => $response['http_code'] ?? null,
            'message' => '',
            'changed' => !empty($data['changed']),
        ];
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string}
     */
    public function setOrderPaid(
        string $baseUrl,
        string $apiKey,
        int $timeoutSeconds,
        string $sourceOrderId
    ): array {
        if ($sourceOrderId === '') {
            return ['ok' => false, 'http_code' => null, 'message' => 'Brak source_order_id.'];
        }

        $url = rtrim(trim($baseUrl), '/') . '/api.php?' . http_build_query([
            'endpoint' => 'orders',
            'action' => 'set_paid',
            'id' => $sourceOrderId,
        ]);

        $response = $this->requestJsonPut($url, $apiKey, $timeoutSeconds, json_encode(['send_email' => 0], JSON_THROW_ON_ERROR));

        return [
            'ok' => ($response['ok'] ?? false) === true,
            'http_code' => $response['http_code'] ?? null,
            'message' => (string) ($response['message'] ?? ''),
        ];
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,data:array<string,mixed>|array<int,mixed>|null}
     */
    private function requestJson(string $url, string $apiKey, int $timeoutSeconds): array
    {
        return $this->executeRequest($url, $apiKey, $timeoutSeconds);
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,data:array<string,mixed>|array<int,mixed>|null}
     */
    private function requestJsonPut(string $url, string $apiKey, int $timeoutSeconds, string $jsonBody): array
    {
        return $this->executeRequest($url, $apiKey, $timeoutSeconds, 'PUT', $jsonBody);
    }

    /**
     * @return array{ok:bool,http_code:int|null,message:string,data:array<string,mixed>|array<int,mixed>|null}
     */
    private function executeRequest(
        string $url,
        string $apiKey,
        int $timeoutSeconds,
        string $method = 'GET',
        ?string $jsonBody = null
    ): array {
        $curl = curl_init($url);
        if ($curl === false) {
            return [
                'ok' => false,
                'http_code' => null,
                'message' => 'Nie udalo sie zainicjalizowac polaczenia HTTP.',
                'data' => null,
            ];
        }

        $headers = [
            'Accept: application/json',
            'X-Api-Key: ' . $apiKey,
        ];

        $opts = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => max(1, min(120, $timeoutSeconds)),
            CURLOPT_CONNECTTIMEOUT => max(1, min(120, $timeoutSeconds)),
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
        ];

        if ($method !== 'GET') {
            $opts[CURLOPT_CUSTOMREQUEST] = $method;
        }

        if ($jsonBody !== null) {
            $opts[CURLOPT_POSTFIELDS] = $jsonBody;
            $headers[] = 'Content-Type: application/json';
        }

        $opts[CURLOPT_HTTPHEADER] = $headers;

        $caPath = SslCertificateResolver::resolve();
        if ($caPath !== null) {
            $opts[CURLOPT_CAINFO] = $caPath;
        }
        curl_setopt_array($curl, $opts);

        $body = curl_exec($curl);
        $httpCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $curlError = trim(curl_error($curl));

        if ($body === false) {
            return [
                'ok' => false,
                'http_code' => $httpCode > 0 ? $httpCode : null,
                'message' => $curlError !== '' ? $curlError : 'Nieznany blad transportu HTTP.',
                'data' => null,
            ];
        }

        $decoded = json_decode((string) $body, true);
        if (!is_array($decoded)) {
            return [
                'ok' => false,
                'http_code' => $httpCode > 0 ? $httpCode : null,
                'message' => 'Odpowiedz API nie jest poprawnym JSON.',
                'data' => null,
            ];
        }

        $apiStatus = trim((string) ($decoded['status'] ?? ''));
        $apiCode = trim((string) ($decoded['code'] ?? ''));
        $apiMessage = trim((string) ($decoded['message'] ?? ''));
        if ($apiStatus !== '' && mb_strtolower($apiStatus) !== 'ok') {
            $message = trim('shopPRO zwrocil blad. ' . $apiCode . ' ' . $apiMessage);
            return [
                'ok' => false,
                'http_code' => $httpCode > 0 ? $httpCode : null,
                'message' => $message !== '' ? $message : 'Nieznany blad API shopPRO.',
                'data' => null,
            ];
        }

        if ($httpCode >= 400) {
            return [
                'ok' => false,
                'http_code' => $httpCode,
                'message' => $apiMessage !== '' ? $apiMessage : 'Blad HTTP podczas komunikacji z shopPRO.',
                'data' => null,
            ];
        }

        $data = $decoded['data'] ?? $decoded;
        if (!is_array($data)) {
            $data = [];
        }

        return [
            'ok' => true,
            'http_code' => $httpCode > 0 ? $httpCode : null,
            'message' => '',
            'data' => $data,
        ];
    }
}
