<?php

declare(strict_types=1);

namespace SeoScope\Services;

use SeoScope\Integrations\GoogleSearchConsoleClient;
use SeoScope\Integrations\GoogleTokenManager;
use SeoScope\Repositories\GscKpiRepository;
use SeoScope\Repositories\SettingsRepository;
use RuntimeException;
use Throwable;

/**
 * Resolves dashboard KPI + daily series: property agg (GSC date parity), then device/country tables, then live API for combined filters.
 */
final class GscKpiViewService
{
    public function __construct(
        private SettingsRepository $settings,
        private GscKpiRepository $kpi
    ) {}

    /**
     * @return array{clicks:int,impressions:int,ctr:float,position:float}
     */
    public function totalsForGlobalFilters(
        int $propertyId,
        string $siteUrl,
        string $startDate,
        string $endDate,
        string $device,
        string $country
    ): array {
        if ($device === 'all' && $country === 'all') {
            return $this->kpi->totalsForRange($propertyId, $startDate, $endDate);
        }
        if ($device !== 'all' && $country === 'all') {
            return $this->kpi->totalsForRangeFromPropertyDevice($propertyId, $startDate, $endDate, $device);
        }
        if ($device === 'all' && $country !== 'all') {
            return $this->kpi->totalsForRangeFromPropertyCountry($propertyId, $startDate, $endDate, $country);
        }
        return $this->totalsFromSearchAnalyticsApi($siteUrl, $startDate, $endDate, $device, $country);
    }

    /**
     * @return list<array{date:string,clicks:int,impressions:int,ctr:float,position:float}>
     */
    public function dailySeriesForGlobalFilters(
        int $propertyId,
        string $siteUrl,
        string $startDate,
        string $endDate,
        string $device,
        string $country
    ): array {
        if ($device === 'all' && $country === 'all') {
            return $this->kpi->dailySeriesForRange($propertyId, $startDate, $endDate);
        }
        if ($device !== 'all' && $country === 'all') {
            return $this->kpi->dailySeriesForRangeFromPropertyDevice($propertyId, $startDate, $endDate, $device);
        }
        if ($device === 'all' && $country !== 'all') {
            return $this->kpi->dailySeriesForRangeFromPropertyCountry($propertyId, $startDate, $endDate, $country);
        }
        return $this->dailySeriesFromSearchAnalyticsApi(
            $siteUrl,
            $startDate,
            $endDate,
            $device,
            $country
        );
    }

    /**
     * @return array{clicks:int,impressions:int,ctr:float,position:float}
     */
    private function totalsFromSearchAnalyticsApi(
        string $siteUrl,
        string $startDate,
        string $endDate,
        string $device,
        string $country
    ): array {
        $siteUrl = trim($siteUrl);
        if ($siteUrl === '') {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        $clientId = trim((string) ($this->settings->get('google.client_id') ?? ''));
        $clientSecret = trim((string) ($this->settings->getSecret('google.client_secret') ?? ''));
        if ($clientId === '' || $clientSecret === '') {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        try {
            $tokens = GoogleTokenManager::ensureFreshTokens($this->settings, $clientId, $clientSecret);
            $accessToken = (string) ($tokens['access_token'] ?? '');
        } catch (RuntimeException) {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        $group = GoogleSearchConsoleClient::dimensionFilterGroupAnd($device, $country);
        if ($group['filters'] === []) {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        try {
            $row = GoogleSearchConsoleClient::searchAnalyticsPropertyTotalRow(
                $siteUrl,
                $accessToken,
                $startDate,
                $endDate,
                ['dimensionFilterGroups' => [$group]]
            );
        } catch (Throwable) {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        if ($row === null) {
            return [
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        $clicks = (int) round((float) ($row['clicks'] ?? 0));
        $impr = (int) round((float) ($row['impressions'] ?? 0));
        $pos = (float) ($row['position'] ?? 0);
        $ctr = (float) ($row['ctr'] ?? 0);
        if ($impr > 0) {
            if ($ctr === 0.0 && $clicks > 0) {
                $ctr = $clicks / $impr;
            }
        } else {
            $ctr = 0.0;
            $pos = 0.0;
        }
        return [
            'clicks' => $clicks,
            'impressions' => $impr,
            'ctr' => $ctr,
            'position' => $pos,
        ];
    }

    /**
     * @return list<array{date:string,clicks:int,impressions:int,ctr:float,position:float}>
     */
    private function dailySeriesFromSearchAnalyticsApi(
        string $siteUrl,
        string $startDate,
        string $endDate,
        string $device,
        string $country
    ): array {
        $siteUrl = trim($siteUrl);
        if ($siteUrl === '') {
            return $this->emptyDailySeries($startDate, $endDate);
        }
        $clientId = trim((string) ($this->settings->get('google.client_id') ?? ''));
        $clientSecret = trim((string) ($this->settings->getSecret('google.client_secret') ?? ''));
        if ($clientId === '' || $clientSecret === '') {
            return $this->emptyDailySeries($startDate, $endDate);
        }
        try {
            $tokens = GoogleTokenManager::ensureFreshTokens($this->settings, $clientId, $clientSecret);
            $accessToken = (string) ($tokens['access_token'] ?? '');
        } catch (RuntimeException) {
            return $this->emptyDailySeries($startDate, $endDate);
        }
        $group = GoogleSearchConsoleClient::dimensionFilterGroupAnd($device, $country);
        if ($group['filters'] === []) {
            return $this->emptyDailySeries($startDate, $endDate);
        }
        $extras = ['dimensionFilterGroups' => [$group]];
        $rows = [];
        try {
            $rows = GoogleSearchConsoleClient::searchAnalyticsAllRows(
                $siteUrl,
                $accessToken,
                $startDate,
                $endDate,
                ['date'],
                25000,
                $extras
            );
        } catch (Throwable) {
            return $this->emptyDailySeries($startDate, $endDate);
        }
        $byDate = [];
        foreach ($rows as $r) {
            if (!is_array($r)) {
                continue;
            }
            $d = (string) (($r['keys'][0] ?? '') ?: '');
            if ($d === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $d)) {
                continue;
            }
            $clicks = (int) round((float) ($r['clicks'] ?? 0));
            $impr = (int) round((float) ($r['impressions'] ?? 0));
            $pos = (float) ($r['position'] ?? 0);
            $ctr = (float) ($r['ctr'] ?? 0);
            if ($impr > 0 && $ctr === 0.0 && $clicks > 0) {
                $ctr = $clicks / $impr;
            }
            $byDate[$d] = [
                'date' => $d,
                'clicks' => $clicks,
                'impressions' => $impr,
                'ctr' => $ctr,
                'position' => $pos,
            ];
        }
        $out = [];
        for ($d = $startDate; $d <= $endDate; $d = date('Y-m-d', strtotime($d . ' +1 day'))) {
            if (isset($byDate[$d])) {
                $out[] = $byDate[$d];
            } else {
                $out[] = [
                    'date' => $d,
                    'clicks' => 0,
                    'impressions' => 0,
                    'ctr' => 0.0,
                    'position' => 0.0,
                ];
            }
        }
        return $out;
    }

    /**
     * @return list<array{date:string,clicks:int,impressions:int,ctr:float,position:float}>
     */
    private function emptyDailySeries(string $startDate, string $endDate): array
    {
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate) || $startDate > $endDate) {
            return [];
        }
        $out = [];
        for ($d = $startDate; $d <= $endDate; $d = date('Y-m-d', strtotime($d . ' +1 day'))) {
            $out[] = [
                'date' => $d,
                'clicks' => 0,
                'impressions' => 0,
                'ctr' => 0.0,
                'position' => 0.0,
            ];
        }
        return $out;
    }
}
