<?php

namespace EasyLinkAutomations\Actions\CleverReach;

if (!defined('ABSPATH')) {
    exit;
}

use EasyLinkAutomations\Auth\Platforms\CleverReachAuth;
use EasyLinkAutomations\Models\Connection;

/**
 * Shared HTTP plumbing for every CleverReach action and controller.
 *
 * Centralises URL building, bearer authentication, JSON encoding, page-based
 * pagination and the CleverReach error envelope so individual actions only
 * describe their own endpoint and payload.
 */
final class CleverReachRequestHelper
{
    private const PLATFORM = 'cleverreach';

    /**
     * Perform a single authenticated CleverReach API request.
     *
     * @return array{success: bool, message: string, data: mixed}
     */
    public static function request(
        int $connectionId,
        string $method,
        string $path,
        array $body = [],
        array $query = []
    ): array {
        $connection = Connection::find($connectionId);
        if (!$connection || ($connection['platform'] ?? '') !== self::PLATFORM) {
            return self::failure('Invalid CleverReach connection');
        }

        $authHeaders = Connection::getAuthHeaders($connectionId);
        $bearer      = trim((string) ($authHeaders['Authorization'] ?? ''));
        if ($bearer === '' || $bearer === 'Bearer') {
            return self::failure('Missing or expired CleverReach access token — reconnect the CleverReach connection');
        }

        $url = self::url($path);
        if ($query) {
            $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($query);
        }

        $args = [
            'method'  => strtoupper($method),
            'headers' => array_merge(
                $authHeaders,
                ['Accept' => 'application/json', 'Content-Type' => 'application/json']
            ),
            'timeout' => 30,
        ];

        if ($body) {
            $args['body'] = wp_json_encode($body);
        }

        $response = wp_remote_request($url, $args);
        if (is_wp_error($response)) {
            return self::failure('CleverReach request failed: ' . $response->get_error_message());
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $raw  = wp_remote_retrieve_body($response);

        $data = null;
        if ($raw !== '') {
            $decoded = json_decode($raw, true);
            $data    = json_last_error() === JSON_ERROR_NONE ? $decoded : $raw;
        }

        // CleverReach reports failures through an { "error": { code, message } }
        // envelope, which can accompany a 2xx status on some endpoints.
        $envelopeError = self::envelopeError($data);
        if ($code < 200 || $code >= 300 || $envelopeError !== '') {
            return [
                'success' => false,
                'message' => self::failureMessage($code, $envelopeError),
                'data'    => $data ?? [],
            ];
        }

        return [
            'success' => true,
            'message' => 'CleverReach request completed successfully',
            'data'    => $data ?? [],
        ];
    }

    /**
     * Retrieve a page-paginated collection, honouring an optional cap.
     *
     * A limit of 0 requests every available page.
     *
     * @return array{success: bool, message: string, data: mixed}
     */
    public static function listPaged(
        int $connectionId,
        string $path,
        array $query = [],
        int $limit = 0,
        int $pageSize = 100
    ): array {
        $items = [];
        $page  = 0;

        // CleverReach caps page size server-side; never ask for more than needed.
        $pageSize = $limit > 0 ? min($pageSize, $limit) : $pageSize;

        do {
            $result = self::request(
                $connectionId,
                'GET',
                $path,
                [],
                array_merge($query, ['page' => $page, 'pagesize' => $pageSize])
            );

            if (!$result['success']) {
                return $result;
            }

            $batch = is_array($result['data']) ? $result['data'] : [];
            if (!$batch) {
                break;
            }

            foreach ($batch as $item) {
                $items[] = $item;
                if ($limit > 0 && count($items) >= $limit) {
                    return self::collection(array_slice($items, 0, $limit));
                }
            }

            $page++;
        } while (count($batch) >= $pageSize);

        return self::collection($items);
    }

    /**
     * Encode a value for safe use as a URL path segment.
     */
    public static function id(mixed $value): string
    {
        return rawurlencode(trim((string) $value));
    }

    /**
     * Clamp a user-supplied limit into a usable range.
     */
    public static function positiveLimit(mixed $value, int $maximum = 1000): int
    {
        return max(1, min($maximum, (int) $value));
    }

    /**
     * Resolve a configured limit, where "return all" disables the cap.
     */
    public static function resultLimit(array $config, int $default = 50, int $maximum = 1000): int
    {
        if (!empty($config['return_all'])) {
            return 0;
        }

        $limit = $config['limit'] ?? '';

        return $limit === '' || $limit === null
            ? $default
            : self::positiveLimit($limit, $maximum);
    }

    /**
     * Build a v3 API URL from a relative path.
     */
    private static function url(string $path): string
    {
        if (str_starts_with($path, 'https://')) {
            return $path;
        }
        if (!str_starts_with($path, '/')) {
            $path = '/' . $path;
        }

        return CleverReachAuth::API_BASE . $path;
    }

    /**
     * Extract a message from the CleverReach error envelope, if present.
     */
    private static function envelopeError(mixed $data): string
    {
        if (!is_array($data) || !isset($data['error'])) {
            return '';
        }

        $error = $data['error'];
        if (is_string($error)) {
            return $error;
        }
        if (is_array($error)) {
            $message = (string) ($error['message'] ?? '');
            $status  = (string) ($error['code'] ?? '');

            return trim($status !== '' && $message !== '' ? $status . ': ' . $message : $message . $status);
        }

        return '';
    }

    private static function failureMessage(int $code, string $details): string
    {
        return 'CleverReach API error (HTTP ' . $code . ')' . ($details !== '' ? ': ' . $details : '');
    }

    /**
     * @return array{success: bool, message: string, data: mixed}
     */
    private static function failure(string $message): array
    {
        return ['success' => false, 'message' => $message, 'data' => []];
    }

    /**
     * @return array{success: bool, message: string, data: mixed}
     */
    private static function collection(array $items): array
    {
        return [
            'success' => true,
            'message' => 'CleverReach request completed successfully',
            'data'    => $items,
        ];
    }
}
