<?php

namespace EasyLinkAutomations\Actions\Rapidmail;

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

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

/**
 * Shared HTTP plumbing for every rapidmail action and controller.
 *
 * rapidmail answers with HAL collections and reports failures using the
 * RFC 7807 problem envelope, so URL building, pagination and error decoding
 * are centralised here rather than repeated per action.
 */
final class RapidmailRequestHelper
{
    private const PLATFORM = 'rapidmail';

    /**
     * Perform a single authenticated rapidmail 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 rapidmail connection');
        }

        $authHeaders = Connection::getAuthHeaders($connectionId);
        $basic       = trim((string) ($authHeaders['Authorization'] ?? ''));
        if ($basic === '' || $basic === 'Basic') {
            return self::failure('Missing rapidmail API credentials on this 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('rapidmail 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;
        }

        if ($code < 200 || $code >= 300) {
            return [
                'success' => false,
                'message' => self::failureMessage($code, $data),
                'data'    => $data ?? [],
            ];
        }

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

    /**
     * Retrieve a HAL collection across pages, 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): array
    {
        $items = [];
        $page  = 1;

        while (true) {
            $result = self::request($connectionId, 'GET', $path, [], array_merge($query, ['page' => $page]));
            if (!$result['success']) {
                return $result;
            }

            $payload = is_array($result['data']) ? $result['data'] : [];

            // Endpoints that paginate their response without accepting a page
            // parameter would otherwise keep returning page 1, duplicating
            // every record once per reported page. Checking before the batch
            // is consumed keeps those duplicates out of the result.
            $reportedPage = isset($payload['page']) ? (int) $payload['page'] : $page;
            if ($reportedPage !== $page) {
                break;
            }

            $batch = self::embedded($payload);

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

            $pageCount = (int) ($payload['page_count'] ?? 0);
            if (!$batch || $pageCount <= 0 || $page >= $pageCount) {
                break;
            }

            $page++;
        }

        return self::collection($items);
    }

    /**
     * Extract the item array from a HAL `_embedded` envelope.
     *
     * The embedded key differs per resource (recipientlists, blacklist,
     * TrxEmail, …), so the first embedded array wins.
     */
    public static function embedded(mixed $payload): array
    {
        if (!is_array($payload)) {
            return [];
        }

        $embedded = $payload['_embedded'] ?? null;
        if (!is_array($embedded)) {
            // Some endpoints answer with a bare list rather than a HAL envelope.
            // array_is_list() is PHP 8.1+ and this plugin supports PHP 8.0.
            if ($payload === [] || array_keys($payload) === range(0, count($payload) - 1)) {
                return $payload;
            }

            return [];
        }

        foreach ($embedded as $value) {
            if (is_array($value)) {
                return $value;
            }
        }

        return [];
    }

    /**
     * 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);
    }

    /**
     * Translate a rapidmail boolean-style query flag.
     */
    public static function yesNo(mixed $value): string
    {
        return empty($value) ? 'no' : 'yes';
    }

    private static function url(string $path): string
    {
        if (str_starts_with($path, 'https://')) {
            return $path;
        }
        if (!str_starts_with($path, '/')) {
            $path = '/' . $path;
        }

        return RapidmailAuth::API_BASE . $path;
    }

    /**
     * Build a message from the RFC 7807 problem envelope rapidmail returns.
     */
    private static function failureMessage(int $code, mixed $data): string
    {
        $details = '';

        if (is_array($data)) {
            $title  = trim((string) ($data['title'] ?? ''));
            $detail = trim((string) ($data['detail'] ?? ''));

            $details = $title;
            if ($detail !== '' && $detail !== $title) {
                $details .= ($details !== '' ? ': ' : '') . $detail;
            }

            // Validation failures list the offending fields separately.
            $validation = $data['validation_messages'] ?? null;
            if (is_array($validation)) {
                $fields = [];
                foreach ($validation as $field => $messages) {
                    $text = is_array($messages) ? implode(', ', array_map('strval', $messages)) : (string) $messages;
                    $fields[] = $field . ' (' . $text . ')';
                }
                if ($fields) {
                    $details .= ($details !== '' ? ' — ' : '') . implode('; ', $fields);
                }
            }
        } elseif (is_string($data)) {
            $details = $data;
        }

        if ($code === 503) {
            $details = $details !== ''
                ? $details
                : 'Rate limit exceeded — rapidmail allows 10 requests per second';
        }

        return 'rapidmail 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' => 'rapidmail request completed successfully',
            'data'    => $items,
        ];
    }
}
