<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Config;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class SemstormService
{
    private Client $http;

    public function __construct()
    {
        $timeout = (float) Config::getDbSetting('semstorm_timeout_seconds', Config::get('SEMSTORM_TIMEOUT_SECONDS', '30'));

        $this->http = new Client([
            'timeout' => max(5, $timeout),
            'verify' => false,
        ]);
    }

    public function fetchDomainMetrics(string $domain, \DateTimeImmutable $metricMonth): array
    {
        $baseUrl = rtrim((string) Config::getDbSetting('semstorm_api_base', Config::get('SEMSTORM_API_BASE', 'https://api.semstorm.com')), '/');
        $login = trim((string) Config::getDbSetting('semstorm_login', Config::get('SEMSTORM_LOGIN', '')));
        $password = trim((string) Config::getDbSetting('semstorm_password', Config::get('SEMSTORM_PASSWORD', '')));

        if ($login === '' || $password === '') {
            throw new \RuntimeException('Brak danych logowania SEMSTORM (login/haslo).');
        }

        $accessToken = $this->requestAccessToken($baseUrl, $login, $password);

        $payload = [
            'domains' => [$domain],
        ];

        try {
            $response = $this->http->post($baseUrl . '/semstorm/v4/explorer/domain-stats', [
                'headers' => [
                    'Accept' => 'application/json',
                    'Content-Type' => 'application/json',
                    'Authorization' => 'Bearer ' . $accessToken,
                    'User-Agent' => 'BackPRO/1.0',
                ],
                'json' => $payload,
            ]);
        } catch (GuzzleException $e) {
            throw new \RuntimeException('Blad pobierania statystyk SEMSTORM: ' . $e->getMessage(), 0, $e);
        }

        $raw = (string) $response->getBody();
        $decoded = json_decode($raw, true);
        if (!is_array($decoded)) {
            throw new \RuntimeException('SEMSTORM zwrocil niepoprawny JSON.');
        }

        $metrics = $this->extractDomainMetrics($decoded, $domain);

        return [
            'top3' => (int) ($metrics['top3'] ?? 0),
            'top10' => (int) ($metrics['top10'] ?? 0),
            'top20' => (int) ($metrics['top20'] ?? 0),
            'top50' => (int) ($metrics['top50'] ?? 0),
            'traffic' => (int) ($metrics['traffic'] ?? 0),
            'payload' => $raw,
        ];
    }

    private function requestAccessToken(string $baseUrl, string $login, string $password): string
    {
        try {
            $response = $this->http->post($baseUrl . '/consumer/login', [
                'headers' => [
                    'Accept' => 'application/json',
                    'User-Agent' => 'BackPRO/1.0',
                ],
                'form_params' => [
                    'username' => $login,
                    'password' => $password,
                ],
            ]);
        } catch (\Throwable $e) {
            throw new \RuntimeException('Nie udalo sie uzyskac tokenu SEMSTORM: ' . $e->getMessage(), 0, $e);
        }

        $decoded = json_decode((string) $response->getBody(), true);
        $token = is_array($decoded) ? (string) ($decoded['token'] ?? '') : '';

        if ($token === '') {
            throw new \RuntimeException('Nie udalo sie uzyskac tokenu SEMSTORM: Brak pola token w odpowiedzi.');
        }

        return $token;
    }

    private function extractDomainMetrics(array $payload, string $requestedDomain): array
    {
        $results = $payload['results'] ?? null;
        if (!is_array($results) || empty($results)) {
            return ['top3' => 0, 'top10' => 0, 'top20' => 0, 'top50' => 0, 'traffic' => 0];
        }

        $normalizedDomain = strtolower($requestedDomain);
        $domainData = null;

        foreach ($results as $domain => $statsByDate) {
            if (!is_array($statsByDate)) {
                continue;
            }

            if (strtolower((string) $domain) === $normalizedDomain) {
                $domainData = $statsByDate;
                break;
            }

            if ($domainData === null) {
                $domainData = $statsByDate;
            }
        }

        if (!is_array($domainData) || empty($domainData)) {
            return ['top3' => 0, 'top10' => 0, 'top20' => 0, 'top50' => 0, 'traffic' => 0];
        }

        $latestDateKey = null;
        $latestDateNumeric = null;
        foreach (array_keys($domainData) as $dateKeyRaw) {
            $dateKey = (string) $dateKeyRaw;
            $dateNumeric = (int) preg_replace('/\D+/', '', $dateKey);
            if ($dateNumeric <= 0) {
                continue;
            }
            if ($latestDateNumeric === null || $dateNumeric > $latestDateNumeric) {
                $latestDateNumeric = $dateNumeric;
                $latestDateKey = $dateKeyRaw;
            }
        }

        if ($latestDateKey === null || !isset($domainData[$latestDateKey]) || !is_array($domainData[$latestDateKey])) {
            return ['top3' => 0, 'top10' => 0, 'top20' => 0, 'top50' => 0, 'traffic' => 0];
        }

        $latest = $domainData[$latestDateKey];
        $keywordsData = [];

        if (is_array($latest['keywords'] ?? null)) {
            $keywordsData = $latest['keywords'];
        } elseif (is_array($latest['keywords_data'] ?? null)) {
            $keywordsData = $latest['keywords_data'];
        }

        return [
            'top3' => max(0, (int) ($keywordsData['top3'] ?? 0)),
            'top10' => max(0, (int) ($keywordsData['top10'] ?? ($latest['keywords_top'] ?? 0))),
            'top20' => max(0, (int) ($keywordsData['top20'] ?? 0)),
            'top50' => max(0, (int) ($keywordsData['top50'] ?? ($latest['keywords'] ?? 0))),
            'traffic' => max(0, (int) ($latest['traffic'] ?? 0)),
        ];
    }
}
