<?php

namespace App\Http\Middleware;

use Closure;

class ApiDebugLogger
{
    // Never echo before $next(): it forces early output and locks Content-Type to text/html.
    private $enabled = true;
    private $allowedIps = ['115.245.200.250',];
    private $maxBytes = 12000;
    private $logFile = 'storage/logs/api-debug.log';

    public function handle($request, Closure $next)
    {
        if (!$this->enabled) {
            return $next($request);
        }

        $clientIp = $this->resolveClientIp($request);
        if (empty($this->allowedIps) || !in_array($clientIp, $this->allowedIps, true)) {
            //return $next($request);
        }

        $start = microtime(true);
        $response = $next($request);
        $durationMs = (int) ((microtime(true) - $start) * 1000);

        $maxBytes = $this->maxBytes > 0 ? $this->maxBytes : 12000;

        $sanitizedRequest = $this->stripDescriptionKeys($request->all());

        $entry = [
            'time' => date('Y-m-d H:i:s'),
            'ip' => $clientIp,
            'method' => strtoupper((string) $request->method()),
            'endpoint' => (string) $request->path(),
            'url' => (string) $request->fullUrl(),
            'status' => method_exists($response, 'getStatusCode') ? (int) $response->getStatusCode() : null,
            'duration_ms' => $durationMs,
            'request_body' => $this->limitString($this->encodeSafe($sanitizedRequest), $maxBytes),
            'response_body' => $this->limitString($this->sanitizeResponseBodyForLog($response), $maxBytes),
        ];

        $logPath = $this->resolveLogPath();
        try {
            @file_put_contents($logPath, json_encode($entry, JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND);
        } catch (\Exception $e) {
            // Intentionally suppress debug logger errors.
        }

        return $response;
    }

    private function resolveLogPath()
    {
        $path = trim((string) $this->logFile);
        if ($path === '') {
            $path = 'storage/logs/api-debug.log';
        }
        if (preg_match('/^[a-zA-Z]:\\\\/', $path) === 1 || strpos($path, '/') === 0 || strpos($path, '\\\\') === 0) {
            return $path;
        }
        return rtrim(base_path(), '\\/') . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
    }

    private function resolveClientIp($request)
    {
        $xff = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? trim((string) $_SERVER['HTTP_X_FORWARDED_FOR']) : '';
        if ($xff !== '') {
            $pieces = explode(',', $xff);
            $candidate = trim((string) $pieces[0]);
            if ($candidate !== '') {
                return $candidate;
            }
        }

        $realIp = isset($_SERVER['HTTP_X_REAL_IP']) ? trim((string) $_SERVER['HTTP_X_REAL_IP']) : '';
        if ($realIp !== '') {
            return $realIp;
        }

        return (string) $request->ip();
    }

    /**
     * Response body as string for logs: JSON payloads have `description` keys stripped recursively.
     *
     * @param mixed $response
     *
     * @return string
     */
    private function sanitizeResponseBodyForLog($response)
    {
        $raw = $this->extractResponseBody($response);
        if (! is_string($raw) || $raw === '') {
            return is_string($raw) ? $raw : '';
        }
        $trim = ltrim($raw);
        if ($trim === '' || ($trim[0] !== '{' && $trim[0] !== '[')) {
            return $raw;
        }
        $decoded = json_decode($raw, true);
        if (json_last_error() !== JSON_ERROR_NONE || ! is_array($decoded)) {
            return $raw;
        }

        return $this->encodeSafe($this->stripDescriptionKeys($decoded));
    }

    private function extractResponseBody($response)
    {
        if (!is_object($response) || !method_exists($response, 'getContent')) {
            return '';
        }

        try {
            $content = $response->getContent();
            if (is_string($content)) {
                return $content;
            }
            return $this->encodeSafe($content);
        } catch (\Exception $e) {
            return '[unavailable response body]';
        }
    }

    /**
     * Remove `description` keys at any depth (arrays only; leaves scalars unchanged).
     *
     * @param mixed $data
     *
     * @return mixed
     */
    private function stripDescriptionKeys($data)
    {
        if (! is_array($data)) {
            return $data;
        }
        if (array_key_exists('description', $data)) {
            unset($data['description']);
        }
        foreach ($data as $k => $v) {
            if (is_array($v)) {
                $data[$k] = $this->stripDescriptionKeys($v);
            }
        }

        return $data;
    }

    private function encodeSafe($value)
    {
        $json = json_encode($value);
        if ($json === false) {
            return '[unencodable payload]';
        }
        return $json;
    }

    private function limitString($value, $maxBytes)
    {
        $text = is_string($value) ? $value : (string) $value;
        if (strlen($text) <= $maxBytes) {
            return $text;
        }
        return substr($text, 0, $maxBytes) . '...[truncated]';
    }
}
