<?php

namespace App\Services\V2026;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Symfony\Component\HttpFoundation\Response;

/**
 * Invokes v2019 controllers and normalizes payloads to the v2026 envelope:
 * { "status": bool, "message": string, "data": mixed }.
 */
class V2026LegacyBridge
{
    /** @var int[] */
    public static $successCodes = [1001];

    public static function paginationParams(Request $request): array
    {
        $page = (int) $request->query('page', $request->input('page', 1));
        $perPage = (int) $request->query('per_page', $request->input('per_page', 20));
        if ($page < 1) {
            $page = 1;
        }
        if ($perPage < 1) {
            $perPage = 20;
        }
        if ($perPage > 100) {
            $perPage = 100;
        }

        return ['page' => $page, 'per_page' => $perPage];
    }

    /**
     * Filters for v2019 POST bodies: JSON in ?filters= or request input array.
     */
    public static function legacyFilters(Request $request): array
    {
        $filters = [];
        $raw = $request->query('filters', $request->input('filters'));
        if (is_string($raw) && $raw !== '') {
            $decoded = json_decode($raw, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                $filters = $decoded;
            }
        } elseif (is_array($raw)) {
            $filters = $raw;
        }

        return $filters;
    }

    /**
     * Base fields commonly read by v2019 controllers.
     */
    public static function legacyAuthAndContext(Request $request): array
    {
        $base = [];
        foreach (['accessToken', 'resourceType', 'visitorID', 'memid', 'appType', 'app_type', 'deviceType', 'deviceID'] as $key) {
            if ($request->has($key)) {
                $base[$key] = $request->input($key);
            }
        }

        return $base;
    }

    public static function mergeLegacyPost(Request $request, array $body): Request
    {
        $payload = array_merge(
            self::legacyAuthAndContext($request),
            $body
        );

        return Request::create('/', 'POST', $payload);
    }

    /**
     * @param  class-string  $controllerClass
     */
    public static function invoke(Request $request, string $controllerClass, string $method, array $postBody = []): mixed
    {
        $sub = self::mergeLegacyPost($request, $postBody);
        $controller = new $controllerClass($sub);

        return $controller->{$method}($sub);
    }

    /**
     * @param  mixed  $legacyReturn  JsonResponse|string|array
     */
    public static function normalizeEnvelope($legacyReturn): array
    {
        if ($legacyReturn instanceof JsonResponse || $legacyReturn instanceof Response) {
            $decoded = json_decode($legacyReturn->getContent(), true);
            if (! is_array($decoded)) {
                return [
                    'ok' => false,
                    'http' => $legacyReturn->getStatusCode(),
                    'message' => 'Invalid legacy JSON response',
                    'data' => null,
                ];
            }

            return self::normalizeDecodedArray($decoded, $legacyReturn->getStatusCode());
        }

        if (is_string($legacyReturn)) {
            $decoded = json_decode($legacyReturn, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return self::normalizeDecodedArray($decoded, 200);
            }

            return [
                'ok' => true,
                'http' => 200,
                'message' => 'Success',
                'data' => $legacyReturn,
            ];
        }

        if (is_array($legacyReturn)) {
            return self::normalizeDecodedArray($legacyReturn, 200);
        }

        return [
            'ok' => false,
            'http' => 500,
            'message' => 'Unexpected legacy response type',
            'data' => null,
        ];
    }

    private static function normalizeDecodedArray(array $decoded, int $httpStatus): array
    {
        if (isset($decoded['status']) && is_array($decoded['status'])) {
            $code = Arr::get($decoded, 'status.code');
            $message = (string) Arr::get($decoded, 'status.message', '');
            if (isset($decoded['status']['data'])) {
                $data = $decoded['status']['data'];

                return [
                    'ok' => self::isSuccessCode($code),
                    'http' => 200,
                    'message' => $message !== '' ? $message : (self::isSuccessCode($code) ? 'Success' : 'Error'),
                    'data' => $data,
                ];
            }

            $data = $decoded['data'] ?? null;
            if ($data === null && count($decoded) > 1) {
                $data = Arr::except($decoded, ['status']);
            }

            return [
                'ok' => self::isSuccessCode($code),
                'http' => 200,
                'message' => $message !== '' ? $message : (self::isSuccessCode($code) ? 'Success' : 'Error'),
                'data' => $data,
            ];
        }

        if (isset($decoded['code']) && ! isset($decoded['status'])) {
            $code = $decoded['code'];
            $message = (string) ($decoded['message'] ?? '');

            return [
                'ok' => self::isSuccessCode($code),
                'http' => 200,
                'message' => $message !== '' ? $message : 'Response',
                'data' => Arr::except($decoded, ['code', 'message']),
            ];
        }

        return [
            'ok' => true,
            'http' => $httpStatus,
            'message' => 'Success',
            'data' => $decoded,
        ];
    }

    private static function isSuccessCode($code): bool
    {
        if ($code === null) {
            return false;
        }

        return in_array((int) $code, self::$successCodes, true);
    }

    /**
     * Client-side pagination for a flat list inside data (mutates envelope data).
     *
     * @param  mixed  $data
     * @return array{0:mixed,1:array|null}
     */
    public static function applyClientPagination($data, int $page, int $perPage): array
    {
        if (! is_array($data)) {
            return [$data, null];
        }
        $keys = array_keys($data);
        $isList = $keys === range(0, count($data) - 1);
        if (! $isList) {
            return [$data, null];
        }

        $total = count($data);
        $perPage = max(1, min($perPage, 100));
        $page = max(1, $page);
        $lastPage = (int) max(1, ceil($total / $perPage));
        $offset = ($page - 1) * $perPage;
        $items = array_values(array_slice($data, $offset, $perPage));
        $meta = [
            'current_page' => $page,
            'per_page' => $perPage,
            'total' => $total,
            'last_page' => $lastPage,
            'from' => $total ? $offset + 1 : 0,
            'to' => (int) min($offset + $perPage, $total),
        ];

        return [$items, $meta];
    }

    /**
     * Approximate "has more" when server-side page size is fixed (e.g. tenders).
     */
    public static function serverPageMeta(int $page, int $perPageRequested, int $itemsReturned, int $serverLimit): array
    {
        return [
            'current_page' => $page,
            'per_page' => $serverLimit,
            'requested_per_page' => $perPageRequested,
            'returned' => $itemsReturned,
            'has_more' => $itemsReturned >= $serverLimit,
        ];
    }

    public static function jsonFromEnvelope(bool $ok, string $message, $data, int $httpStatus = 200): JsonResponse
    {
        return new JsonResponse([
            'status' => $ok,
            'message' => $message,
            'data' => $data === null ? new \stdClass() : $data,
        ], $httpStatus);
    }

    /**
     * @param  array{client_paginate?:bool,server_meta?:array|null}  $options
     */
    public static function respondFromLegacy($legacyReturn, Request $request, array $options = []): JsonResponse
    {
        $norm = self::normalizeEnvelope($legacyReturn);
        $data = $norm['data'];

        if (! empty($options['server_meta']) && is_array($options['server_meta'])) {
            $items = is_array($data) ? $data : ($data !== null ? [$data] : []);
            $data = [
                'items' => $items,
                'pagination' => $options['server_meta'],
            ];
        } elseif (! empty($options['client_paginate']) && is_array($data)) {
            $keys = array_keys($data);
            $isList = $keys === range(0, count($data) - 1);
            if ($isList) {
                $p = self::paginationParams($request);
                [$items, $pagination] = self::applyClientPagination($data, $p['page'], $p['per_page']);
                $data = [
                    'items' => $items,
                    'pagination' => $pagination,
                ];
            }
        }

        return self::jsonFromEnvelope($norm['ok'], $norm['message'], $data, 200);
    }
}

