<?php

namespace AiChat\Services;

use LLPhant\Exception\HttpException;

class LlmErrorResponseDecorator
{
    public static function decorateError(\Exception $exception, \LLMProviderType $provider): array
    {
        // only handle HttpException from LLPhant
        if (!$exception instanceof HttpException) {
            return self::createUnknownError($provider, $exception);
        }

        $message = $exception->getMessage();

        // extract status code and JSON body from exception message
        // format: "HTTP error from AI engine (404): {...}" or "HTTP error Anthropic (401): {...}"
        if (!preg_match('/\((\d+)\): (.+)$/s', $message, $matches)) {
            return self::createUnknownError($provider, $exception);
        }

        $statusCode = (int)$matches[1];
        $jsonBody = $matches[2];

        // try to decode JSON response
        $errorData = json_decode($jsonBody, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            // if JSON parsing fails, return raw message
            return self::createUnknownError($provider, $exception, $statusCode);
        }

        return match ($provider) {
            \LLMProviderType::OPENAI, \LLMProviderType::OPEANAILIKE => self::parseOpenAIError($errorData, $statusCode),
            \LLMProviderType::ANTHROPIC => self::parseAnthropicError($errorData, $statusCode),
            \LLMProviderType::GEMINI => self::parseGeminiError($errorData, $statusCode),
            \LLMProviderType::MISTRAL => self::parseMistralError($errorData, $statusCode),
            default => self::createUnknownError($provider, $exception, $statusCode)
        };
    }

    private static function parseOpenAIError(array $errorData, int $statusCode): array
    {
        $error = $errorData['error'] ?? [];

        return [
            'error' => [
                'message' => $error['message'] ?? 'Unknown error from OpenAI',
                'code' => $error['code'] ?? null,
                'status' => $statusCode
            ]
        ];
    }

    private static function parseAnthropicError(array $errorData, int $statusCode): array
    {
        $error = $errorData['error'] ?? [];

        return [
            'error' => [
                'message' => $error['message'] ?? 'Unknown error from Anthropic',
                'code' => $error['type'] ?? null,
                'status' => $statusCode
            ]
        ];
    }

    private static function parseGeminiError(array $errorData, int $statusCode): array
    {
        if (isset($errorData[0])) {
            $errorData = $errorData[0];
        }

        $error = $errorData['error'] ?? [];

        $reason = null;
        if (isset($error['details']) && is_array($error['details'])) {
            foreach ($error['details'] as $detail) {
                if (isset($detail['reason'])) {
                    $reason = $detail['reason'];
                    break;
                }
            }
        }

        return [
            'error' => [
                'message' => $error['message'] ?? 'Unknown error from Gemini',
                'code' => $reason ?? $error['status'] ?? null,
                'status' => $error['code'] ?? $statusCode
            ]
        ];
    }

    private static function parseMistralError(array $errorData, int $statusCode): array
    {
        $message = $errorData['detail'] ?? 'Unknown error from Mistral';

        return [
            'error' => [
                'message' => $message,
                'code' => null,
                'status' => $statusCode
            ]
        ];
    }

    private static function createUnknownError(
        \LLMProviderType $provider,
        \Exception $exception,
        ?int $statusCode = null
    ): array {
        return [
            'error' => [
                'message' => $exception->getMessage(),
                'code' => null,
                'status' => $statusCode ?? 500
            ]
        ];
    }

    public static function shouldDecorate(\Exception $exception): bool
    {
        return $exception instanceof HttpException;
    }
}
