<?php

namespace App\Services;

use App\Models\ApiSetting;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Throwable;

class FmpEconomicCalendarService
{
    private const ENDPOINT = 'https://financialmodelingprep.com/stable/economic-calendar';

    /**
     * Fetch economic calendar data from Financial Modeling Prep.
     *
     * The API key stays in Laravel config/.env and is never exposed to Blade or JavaScript.
     *
     * @param  array{date?: string, country?: string, currency?: string}  $filters
     * @return array{events: array<int, array<string, mixed>>, status: string, message: string|null, api_key_configured: bool}
     */
    public function economicCalendar(array $filters = []): array
    {
        $apiKey = $this->apiKey();

        if (! filled($apiKey)) {
            return $this->result(
                [],
                'missing_key',
                'FMP API key is not connected yet.',
                false
            );
        }

        try {
            $response = Http::timeout(12)
                ->retry(1, 200)
                ->acceptJson()
                ->get(self::ENDPOINT, $this->query($apiKey, $filters));
        } catch (ConnectionException) {
            return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
        } catch (Throwable) {
            return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
        }

        if (in_array($response->status(), [401, 403], true)) {
            return $this->result([], 'invalid_key', 'FMP API key is invalid or unauthorized.');
        }

        if ($response->status() === 402) {
            return $this->result([], 'restricted_endpoint', 'FMP economic calendar is restricted for your current subscription.');
        }

        if ($response->status() === 429) {
            return $this->result([], 'limit_reached', 'FMP API limit reached. Please check your plan or try again later.');
        }

        if ($response->serverError()) {
            return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
        }

        if ($response->failed()) {
            return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
        }

        $data = $response->json();

        if (! is_array($data)) {
            return $this->messageFromBody($response->body());
        }

        if ($this->containsProviderError($data)) {
            return $this->providerErrorResult($data);
        }

        $events = collect($this->eventRows($data))
            ->filter(fn ($event): bool => is_array($event))
            ->map(fn (array $event): array => $this->normalizeEvent($event))
            ->filter(fn (array $event): bool => $this->passesLocalFilters($event, $filters))
            ->values()
            ->all();

        return $this->result(
            $events,
            'success',
            count($events) > 0 ? null : 'No economic events found for this filter.'
        );
    }

    private function query(string $apiKey, array $filters): array
    {
        $query = [
            'apikey' => $apiKey,
        ];

        if (filled($filters['date'] ?? null)) {
            $query['from'] = $filters['date'];
            $query['to'] = $filters['date'];
        }

        return $query;
    }

    private function apiKey(): ?string
    {
        $databaseSetting = ApiSetting::activeForProvider(ApiSetting::PROVIDER_FMP);

        if ($databaseSetting) {
            return $databaseSetting->decryptedApiKey();
        }

        return config('services.fmp.key');
    }

    private function eventRows(array $data): array
    {
        if (array_is_list($data)) {
            return $data;
        }

        foreach (['data', 'calendar', 'economicCalendar', 'events'] as $key) {
            if (isset($data[$key]) && is_array($data[$key])) {
                return $data[$key];
            }
        }

        return [$data];
    }

    private function normalizeEvent(array $event): array
    {
        $impact = $event['impact']
            ?? $event['importance']
            ?? $event['Importance']
            ?? $event['level']
            ?? null;

        return [
            'datetime' => $event['date']
                ?? $event['Date']
                ?? $event['datetime']
                ?? $event['time']
                ?? null,
            'country' => $event['country']
                ?? $event['Country']
                ?? null,
            'currency' => $event['currency']
                ?? $event['Currency']
                ?? null,
            'event' => $event['event']
                ?? $event['Event']
                ?? $event['title']
                ?? $event['name']
                ?? null,
            'impact' => $this->impactLabel($impact),
            'actual' => $event['actual']
                ?? $event['Actual']
                ?? null,
            'forecast' => $event['forecast']
                ?? $event['Forecast']
                ?? $event['estimate']
                ?? $event['Estimate']
                ?? null,
            'previous' => $event['previous']
                ?? $event['Previous']
                ?? null,
        ];
    }

    private function impactLabel(mixed $impact): string
    {
        if ($impact === null || $impact === '') {
            return 'Not Available';
        }

        $impact = Str::lower((string) $impact);

        return match (true) {
            in_array($impact, ['1', 'low'], true) => 'Low',
            in_array($impact, ['2', 'medium', 'med'], true) => 'Medium',
            in_array($impact, ['3', 'high'], true) => 'High',
            default => Str::title($impact),
        };
    }

    private function passesLocalFilters(array $event, array $filters): bool
    {
        if (filled($filters['country'] ?? null)) {
            $eventCountry = Str::lower((string) $event['country']);
            $filterCountry = Str::lower($filters['country']);

            if (! Str::contains($eventCountry, $filterCountry)) {
                return false;
            }
        }

        if (filled($filters['currency'] ?? null) && strtoupper((string) $event['currency']) !== strtoupper($filters['currency'])) {
            return false;
        }

        return true;
    }

    private function containsProviderError(array $data): bool
    {
        return collect($data)
            ->filter(fn ($value, $key): bool => is_string($key) && Str::contains(Str::lower($key), ['error', 'message', 'limit']))
            ->isNotEmpty();
    }

    private function providerErrorResult(array $data): array
    {
        $message = Str::lower(json_encode($data) ?: '');

        if (Str::contains($message, ['invalid', 'unauthorized', 'forbidden', 'api key'])) {
            return $this->result([], 'invalid_key', 'FMP API key is invalid or unauthorized.');
        }

        if (Str::contains($message, ['restricted endpoint', 'subscription', 'not available under your current subscription'])) {
            return $this->result([], 'restricted_endpoint', 'FMP economic calendar is restricted for your current subscription.');
        }

        if (Str::contains($message, ['limit', 'rate', 'upgrade', 'plan'])) {
            return $this->result([], 'limit_reached', 'FMP API limit reached. Please check your plan or try again later.');
        }

        return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
    }

    private function messageFromBody(string $body): array
    {
        $body = Str::lower($body);

        if (Str::contains($body, ['invalid', 'unauthorized', 'forbidden', 'api key'])) {
            return $this->result([], 'invalid_key', 'FMP API key is invalid or unauthorized.');
        }

        if (Str::contains($body, ['restricted endpoint', 'subscription', 'not available under your current subscription'])) {
            return $this->result([], 'restricted_endpoint', 'FMP economic calendar is restricted for your current subscription.');
        }

        if (Str::contains($body, ['limit', 'rate', 'upgrade', 'plan'])) {
            return $this->result([], 'limit_reached', 'FMP API limit reached. Please check your plan or try again later.');
        }

        return $this->result([], 'api_error', 'Unable to load economic calendar data right now.');
    }

    private function result(array $events, string $status, ?string $message, bool $apiKeyConfigured = true): array
    {
        return [
            'events' => $events,
            'status' => $status,
            'message' => $message,
            'api_key_configured' => $apiKeyConfigured,
        ];
    }
}
