<?php

namespace App\Services\ApiExplorer;

use Illuminate\Support\Facades\Http;

/**
 * API 요청 프록시 서비스
 */
class ApiRequestService
{
    /**
     * API 요청 실행
     *
     * @return array{status: int, headers: array, body: mixed, duration_ms: int}
     */
    public function execute(
        string $method,
        string $url,
        array $headers = [],
        array $query = [],
        ?array $body = null
    ): array {
        // URL 유효성 검사
        $this->validateUrl($url);

        $startTime = microtime(true);

        try {
            $request = Http::timeout(config('api-explorer.proxy.timeout', 30))
                ->withHeaders($headers);

            // 쿼리 파라미터 추가
            if (! empty($query)) {
                $url = $this->appendQueryParams($url, $query);
            }

            // 요청 실행
            $response = match (strtoupper($method)) {
                'GET' => $request->get($url),
                'POST' => $request->post($url, $body ?? []),
                'PUT' => $request->put($url, $body ?? []),
                'PATCH' => $request->patch($url, $body ?? []),
                'DELETE' => $request->delete($url, $body ?? []),
                'HEAD' => $request->head($url),
                'OPTIONS' => $request->send('OPTIONS', $url),
                default => throw new \InvalidArgumentException("지원하지 않는 HTTP 메서드: {$method}"),
            };

            $durationMs = (int) ((microtime(true) - $startTime) * 1000);

            return [
                'status' => $response->status(),
                'headers' => $response->headers(),
                'body' => $this->parseResponseBody($response),
                'duration_ms' => $durationMs,
            ];
        } catch (\Illuminate\Http\Client\ConnectionException $e) {
            $durationMs = (int) ((microtime(true) - $startTime) * 1000);

            return [
                'status' => 0,
                'headers' => [],
                'body' => [
                    'error' => true,
                    'message' => '연결 실패: '.$e->getMessage(),
                ],
                'duration_ms' => $durationMs,
            ];
        } catch (\Exception $e) {
            $durationMs = (int) ((microtime(true) - $startTime) * 1000);

            return [
                'status' => 0,
                'headers' => [],
                'body' => [
                    'error' => true,
                    'message' => '요청 오류: '.$e->getMessage(),
                ],
                'duration_ms' => $durationMs,
            ];
        }
    }

    /**
     * URL 유효성 검사 (화이트리스트)
     */
    private function validateUrl(string $url): void
    {
        $allowedHosts = config('api-explorer.proxy.allowed_hosts', []);

        if (empty($allowedHosts)) {
            return; // 화이트리스트 미설정 시 모든 호스트 허용
        }

        $parsedUrl = parse_url($url);
        $host = $parsedUrl['host'] ?? '';

        if (! in_array($host, $allowedHosts)) {
            throw new \InvalidArgumentException("허용되지 않은 호스트: {$host}");
        }
    }

    /**
     * 쿼리 파라미터 추가
     */
    private function appendQueryParams(string $url, array $query): string
    {
        // 빈 값 제거
        $query = array_filter($query, fn ($v) => $v !== null && $v !== '');

        if (empty($query)) {
            return $url;
        }

        $separator = str_contains($url, '?') ? '&' : '?';

        return $url.$separator.http_build_query($query);
    }

    /**
     * 응답 본문 파싱
     */
    private function parseResponseBody($response): mixed
    {
        $contentType = $response->header('Content-Type') ?? '';
        $body = $response->body();

        // JSON 응답
        if (str_contains($contentType, 'application/json')) {
            $decoded = json_decode($body, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return $decoded;
            }
        }

        // 텍스트 응답 (최대 크기 제한)
        $maxSize = config('api-explorer.proxy.max_body_size', 1024 * 1024);
        if (strlen($body) > $maxSize) {
            return [
                'truncated' => true,
                'message' => '응답이 너무 큽니다. (최대 '.number_format($maxSize / 1024).'KB)',
                'size' => strlen($body),
            ];
        }

        return $body;
    }

    /**
     * 민감 헤더 마스킹
     */
    public function maskSensitiveHeaders(array $headers): array
    {
        $sensitiveHeaders = config('api-explorer.security.mask_sensitive_headers', []);
        $masked = [];

        foreach ($headers as $key => $value) {
            if (in_array($key, $sensitiveHeaders, true)) {
                $masked[$key] = '***MASKED***';
            } else {
                $masked[$key] = $value;
            }
        }

        return $masked;
    }

    /**
     * 경로 파라미터 치환
     */
    public function substitutePathParams(string $path, array $params): string
    {
        foreach ($params as $key => $value) {
            $path = str_replace('{'.$key.'}', $value, $path);
        }

        return $path;
    }

    /**
     * 변수 치환 ({{VARIABLE}} 패턴)
     */
    public function substituteVariables(string $text, array $variables): string
    {
        foreach ($variables as $key => $value) {
            $text = str_replace('{{'.$key.'}}', $value, $text);
        }

        return $text;
    }
}
