<?php
namespace Analytics\Lib;

/**
 * AI Overview Helper — DataforSEO API integration and scoring utilities.
 * Adapted from ai-tool includes, Redis removed, credentials from .env via $_ENV.
 */
class AiOverviewHelper
{
    private static function credentials(): array
    {
        return [
            'login'    => $_ENV['PPC_DATAFORSEO_LOGIN']    ?? '',
            'password' => $_ENV['PPC_DATAFORSEO_PASSWORD'] ?? '',
        ];
    }

    // ── Model listing ────────────────────────────────────────────────────────

    public static function getModels(string $platform, bool $allModels = false): array
    {
        $platformMap = [
            'ChatGPT'       => 'chat_gpt',
            'Claude'        => 'claude',
            'Gemini'        => 'gemini',
            'Perplexity'    => 'perplexity',
            'Google AI Mode'=> 'google_ai_mode',
        ];
        $apiPlatform = $platformMap[$platform] ?? 'chat_gpt';

        if ($apiPlatform === 'google_ai_mode') {
            return [['name' => 'Search Generative Experience (SGE)', 'web_search_supported' => true]];
        }

        $creds = self::credentials();
        $url   = "https://api.dataforseo.com/v3/ai_optimization/{$apiPlatform}/llm_responses/models";

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_USERPWD        => $creds['login'] . ':' . $creds['password'],
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
            CURLOPT_TIMEOUT        => 15,
        ]);
        $response  = curl_exec($ch);
        $httpCode  = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200 || !$response) {
            return [];
        }

        $data   = json_decode($response, true);
        $models = [];

        if (isset($data['tasks'][0]['result']) && is_array($data['tasks'][0]['result'])) {
            foreach ($data['tasks'][0]['result'] as $item) {
                if (isset($item['model_name'])) {
                    $models[] = [
                        'name'                => $item['model_name'],
                        'web_search_supported'=> $item['web_search_supported'] ?? false,
                    ];
                }
            }
        }

        return $allModels ? $models : self::selectBestModel($models, $apiPlatform);
    }

    private static function selectBestModel(array $models, string $apiPlatform): array
    {
        if (empty($models)) return [];

        $versionPattern = '/-(?:\d{4}-\d{2}-\d{2}|\d{8}|\d{4}|\d{3}|preview|chat-latest|latest|preview-\d{2}-\d{4})$/';
        $grouped = [];

        foreach ($models as $m) {
            $baseName = $m['name'];
            while (preg_match($versionPattern, $baseName)) {
                $baseName = preg_replace($versionPattern, '', $baseName);
            }
            if (!isset($grouped[$baseName])) {
                $grouped[$baseName] = $m;
            } elseif (strlen($m['name']) < strlen($grouped[$baseName]['name']) && $grouped[$baseName]['name'] !== $baseName) {
                $grouped[$baseName] = $m;
            }
        }

        uasort($grouped, function (array $a, array $b) use ($apiPlatform): int {
            $nameA = strtolower($a['name']);
            $nameB = strtolower($b['name']);
            $tiers = ['opus' => 10, 'pro' => 9, 'sonnet' => 8, 'gpt' => 7, 'flash' => 5, 'haiku' => 4, 'nano' => 3, 'lite' => 2];

            $t1 = 0; $t2 = 0;
            foreach ($tiers as $k => $v) { if (strpos($nameA, $k) !== false) { $t1 = $v; break; } }
            foreach ($tiers as $k => $v) { if (strpos($nameB, $k) !== false) { $t2 = $v; break; } }

            if ($apiPlatform === 'gemini') {
                if ($t1 === 9 && $t2 !== 9) return -1;
                if ($t2 === 9 && $t1 !== 9) return  1;
            }

            preg_match('/(\d+(?:\.\d+)?)/', $nameA, $m1);
            preg_match('/(\d+(?:\.\d+)?)/', $nameB, $m2);
            $v1 = isset($m1[1]) ? (float)$m1[1] : 0;
            $v2 = isset($m2[1]) ? (float)$m2[1] : 0;
            if ($v1 !== $v2) return $v2 <=> $v1;
            if ($t1 !== $t2) return $t2 <=> $t1;
            return strlen($nameA) <=> strlen($nameB);
        });

        $best = array_values($grouped)[0] ?? null;
        return $best ? [$best] : [];
    }

    // ── Run a prompt ─────────────────────────────────────────────────────────

    public static function runPrompt(
        string $promptText,
        string $platformLabel,
        string $modelVersion,
        bool   $webSearchSupported,
        string $countryCode
    ): array {
        $platformMap = [
            'ChatGPT'        => 'chat_gpt',
            'Claude'         => 'claude',
            'Gemini'         => 'gemini',
            'Perplexity'     => 'perplexity',
            'Google AI Mode' => 'google_ai_mode',
        ];
        $platform = $platformMap[$platformLabel] ?? 'chat_gpt';

        $locationMap = [
            'IN' => 'India', 'US' => 'United States', 'GB' => 'United Kingdom',
            'CA' => 'Canada', 'AU' => 'Australia', 'AE' => 'United Arab Emirates',
            'SG' => 'Singapore', 'DE' => 'Germany',
        ];
        $locationName = $locationMap[$countryCode] ?? 'India';

        if ($platform === 'google_ai_mode') {
            $url  = 'https://api.dataforseo.com/v3/serp/google/ai_mode/live/advanced';
            $task = [
                'keyword'               => $promptText,
                'location_name'         => $locationName,
                'language_name'         => 'English',
                'device'                => 'desktop',
                'visual_elements'       => true,
                'calculate_rectangles'  => true,
            ];
        } else {
            $url  = "https://api.dataforseo.com/v3/ai_optimization/{$platform}/llm_responses/live";
            $task = ['user_prompt' => $promptText, 'model_name' => $modelVersion];

            if ($webSearchSupported) {
                $task['web_search'] = true;
                if ($platform === 'chat_gpt') {
                    $task['web_search_country_iso_code'] = $countryCode;
                }
            }
        }

        $creds = self::credentials();
        $ch    = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL             => $url,
            CURLOPT_RETURNTRANSFER  => true,
            CURLOPT_USERPWD         => $creds['login'] . ':' . $creds['password'],
            CURLOPT_POST            => true,
            CURLOPT_POSTFIELDS      => json_encode([$task]),
            CURLOPT_HTTPHEADER      => ['Content-Type: application/json'],
            CURLOPT_TIMEOUT         => 120,   // DataforSEO can take 60–90 s under load
            CURLOPT_CONNECTTIMEOUT  => 15,    // fail fast if the server is unreachable
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_error($ch);
        curl_close($ch);

        if ($curlErr) {
            throw new \RuntimeException("DataforSEO connection error: {$curlErr}");
        }

        if ($httpCode !== 200 || !$response) {
            throw new \RuntimeException("DataforSEO API error (HTTP {$httpCode})");
        }

        $data = json_decode($response, true);

        if (!isset($data['tasks'][0])) {
            throw new \RuntimeException('Invalid API response structure');
        }
        if (($data['tasks'][0]['status_code'] ?? 20000) !== 20000) {
            throw new \RuntimeException('API error: ' . ($data['tasks'][0]['status_message'] ?? 'Unknown'));
        }
        if (empty($data['tasks'][0]['result'])) {
            throw new \RuntimeException('No results in API response');
        }

        $resultItem   = $data['tasks'][0]['result'][0];
        $aiText       = 'No text response available.';
        $references   = [];
        $snapshotUrl  = $resultItem['screenshot_url'] ?? null;

        if ($platform === 'google_ai_mode') {
            foreach ($resultItem['items'] ?? [] as $item) {
                if ($item['type'] === 'ai_overview') {
                    $aiText     = $item['text'] ?? $item['markdown'] ?? 'AI Overview generated.';
                    $references = $item['links'] ?? $item['references'] ?? [];
                    break;
                }
            }
        } else {
            // Collect full text by concatenating all sections across all items
            $textParts = [];
            foreach ($resultItem['items'] ?? [] as $item) {
                foreach ($item['sections'] ?? [] as $section) {
                    if (!empty($section['text'])) {
                        $textParts[] = $section['text'];
                    }
                    if (!empty($section['annotations'])) {
                        $references = array_merge($references, $section['annotations']);
                    }
                }
                if (!empty($item['references'])) {
                    $references = array_merge($references, $item['references']);
                }
            }

            if (!empty($textParts)) {
                $aiText = implode("\n\n", $textParts);
            } elseif (!empty($resultItem['answer'])) {
                $aiText = $resultItem['answer'];
            } elseif (!empty($resultItem['text'])) {
                $aiText = $resultItem['text'];
            } elseif (!empty($resultItem['content'])) {
                $aiText = $resultItem['content'];
            }

            if (empty($references)) {
                $references = $resultItem['references'] ?? $resultItem['sources'] ?? [];
            }
        }

        $links = [];
        $rank  = 1;
        foreach ($references as $ref) {
            $links[] = [
                'rank'   => $rank++,
                'title'  => $ref['title'] ?? 'No Title',
                'url'    => $ref['url'] ?? '',
                'domain' => parse_url($ref['url'] ?? '', PHP_URL_HOST) ?? 'Unknown',
            ];
        }

        if (!empty($links)) {
            self::resolveGroundingLinks($links);
        }

        // Deduplicate
        $unique   = [];
        $seen     = [];
        $newRank  = 1;
        foreach ($links as $link) {
            if (!empty($link['url']) && !in_array($link['url'], $seen, true)) {
                $seen[]         = $link['url'];
                $link['rank']   = $newRank++;
                $unique[]       = $link;
            }
        }

        return [
            'ai_response'  => $aiText,
            'links'        => $unique,
            'snapshot_url' => $snapshotUrl,
            'model_label'  => $platformLabel . ' (' . $modelVersion . ')',
        ];
    }

    private static function resolveGroundingLinks(array &$links): void
    {
        $mh      = curl_multi_init();
        $handles = [];

        foreach ($links as $key => $link) {
            if (strpos($link['url'], 'grounding-api-redirect') !== false) {
                $ch = curl_init();
                curl_setopt_array($ch, [
                    CURLOPT_URL            => $link['url'],
                    CURLOPT_HEADER         => true,
                    CURLOPT_NOBODY         => true,
                    CURLOPT_FOLLOWLOCATION => false,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_TIMEOUT        => 10,
                ]);
                curl_multi_add_handle($mh, $ch);
                $handles[$key] = $ch;
            }
        }

        if (empty($handles)) { curl_multi_close($mh); return; }

        $active = null;
        do {
            $mrc = curl_multi_exec($mh, $active);
            if ($active) curl_multi_select($mh);
        } while ($active > 0 && $mrc === CURLM_OK);

        foreach ($handles as $key => $ch) {
            $resp = curl_multi_getcontent($ch);
            $info = curl_getinfo($ch);
            if (in_array($info['http_code'], [301, 302], true) && preg_match('/location: (.*)/i', $resp, $m)) {
                $realUrl             = trim($m[1]);
                $links[$key]['url']  = $realUrl;
                $links[$key]['domain'] = parse_url($realUrl, PHP_URL_HOST) ?? $links[$key]['domain'];
            }
            curl_multi_remove_handle($mh, $ch);
            curl_close($ch);
        }
        curl_multi_close($mh);
    }

    // ── Metric calculation ────────────────────────────────────────────────────

    public static function calcBrandMentions(string $aiResponse, array $nameVariations): int
    {
        $clean = strtolower($aiResponse);
        $count = 0;
        foreach ($nameVariations as $name) {
            if (trim($name) === '') continue;
            $count += substr_count($clean, strtolower($name));
        }
        return $count;
    }

    public static function calcDomainCitations(array $links, array $domainVariations): int
    {
        $normalised = [];
        foreach ($domainVariations as $dom) {
            if (trim($dom) === '') continue;
            $d = strtolower(trim($dom));
            $d = preg_replace('#^https?://#', '', $d);
            $d = rtrim($d, '/');
            $d = preg_replace('/^www\./', '', $d);
            if ($d !== '') $normalised[] = $d;
        }
        $normalised = array_unique($normalised);

        $count = 0;
        foreach ($links as $link) {
            $linkCmp = strtolower($link['url'] ?? '');
            $linkCmp = preg_replace('#^https?://#', '', $linkCmp);
            $linkCmp = preg_replace('/^www\./', '', $linkCmp);
            foreach ($normalised as $dom) {
                if (strpos($linkCmp, $dom) !== false) { $count++; break; }
            }
        }
        return $count;
    }

    public static function calcVisibilityScore(int $brandMentions, int $citationCount, int $totalCitations): int
    {
        if ($brandMentions <= 0 && $citationCount <= 0) return 0;
        $mentionScore  = $brandMentions > 0 ? 15 + min(35, ($brandMentions - 1) * 10) : 0;
        $citationRatio = $totalCitations > 0 ? $citationCount / $totalCitations : 0;
        $citationScore = $citationCount > 0 ? 10 + min(40, ($citationCount * 15) + ($citationRatio * 25)) : 0;
        return (int) max(1, min(100, $mentionScore + $citationScore));
    }

    public static function calcIntentDistribution(string $promptText): array
    {
        $text    = strtolower($promptText);
        $scores  = ['info' => 0, 'nav' => 0, 'comm' => 0, 'tran' => 0, 'task' => 0];
        $signals = [
            'info' => ['what', 'how', 'why', 'guide', 'tutorial', 'meaning', 'example', 'tips', 'learn', 'history'],
            'nav'  => ['login', 'official', 'contact', 'address', 'website', 'customer service', 'portal', 'account'],
            'comm' => ['best', 'top', 'vs', 'compare', 'review', 'alternative', 'comparison', 'ratings', 'versus'],
            'tran' => ['buy', 'price', 'cost', 'cheap', 'order', 'purchase', 'discount', 'coupon', 'sale', 'hire'],
            'task' => ['write', 'analyze', 'code', 'generate', 'summarize', 'create', 'calculate', 'draft', 'design', 'fix'],
        ];
        foreach ($signals as $type => $keywords) {
            foreach ($keywords as $kw) {
                if (strpos($text, $kw) !== false) $scores[$type] += 20;
            }
        }
        if (strpos($text, '?') !== false) $scores['info'] += 10;
        foreach ($scores as $k => $v) $scores[$k] = max($v, 2);
        $total = array_sum($scores);
        if ($total === 0) return ['info' => 30, 'nav' => 15, 'comm' => 20, 'tran' => 20, 'task' => 15];
        foreach ($scores as $k => $v) $scores[$k] = (int) round(($v / $total) * 100);
        $diff    = 100 - array_sum($scores);
        $maxKey  = array_keys($scores, max($scores))[0];
        $scores[$maxKey] += $diff;
        return $scores;
    }

    // ── Domain normalisation helper ───────────────────────────────────────────

    public static function normaliseDomain(string $url): string
    {
        $host = parse_url($url, PHP_URL_HOST) ?: $url;
        $host = strtolower(trim($host));
        return preg_replace('/^www\./', '', $host);
    }
}
