<?php
/**
 * ===========================================
 * FLOWBOT DCI - BING SEARCH ADAPTER
 * ===========================================
 * Microsoft Bing Web Search API adapter
 *
 * Requirements:
 * - API Key from Azure Portal
 *
 * Pricing:
 * - Free tier: 1000 transactions/month
 * - S1: $3 per 1000 transactions
 *
 * Setup:
 * 1. Go to Azure Portal
 * 2. Create a Bing Search resource
 * 3. Get the API key from Keys and Endpoint
 */

declare(strict_types=1);

namespace FlowbotDCI\Services\SearchEngine;

class BingAdapter implements SearchEngineInterface
{
    const NAME = 'Bing';
    const API_URL = 'https://api.bing.microsoft.com/v7.0/search';

    private ?string $apiKey;
    private ?string $lastError = null;
    private int $timeout = 10;
    private string $market = 'en-US';

    public function __construct(array $config = [])
    {
        $this->apiKey = $config['api_key'] ?? null;
        $this->timeout = $config['timeout'] ?? 10;
        $this->market = $config['market'] ?? 'en-US';
    }

    public function getName(): string
    {
        return self::NAME;
    }

    public function isAvailable(): bool
    {
        return !empty($this->apiKey);
    }

    public function search(string $query, int $maxResults = 10, int $offset = 0): array
    {
        $this->lastError = null;

        if (!$this->isAvailable()) {
            $this->lastError = 'Bing API key not configured';
            return [];
        }

        $results = [];

        try {
            // Bing API allows up to 50 results per request
            $count = min($maxResults, 50);

            $params = [
                'q' => $query,
                'count' => $count,
                'offset' => $offset,
                'mkt' => $this->market,
                'responseFilter' => 'Webpages',
                'textDecorations' => 'false',
                'textFormat' => 'Raw',
            ];

            $url = self::API_URL . '?' . http_build_query($params);

            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => $this->timeout,
                CURLOPT_HTTPHEADER => [
                    'Ocp-Apim-Subscription-Key: ' . $this->apiKey,
                    'Accept: application/json',
                ],
            ]);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);

            if ($httpCode !== 200) {
                $this->lastError = "HTTP {$httpCode}: " . ($error ?: 'Request failed');

                // Try to parse error from response
                $data = json_decode($response, true);
                if (isset($data['error']['message'])) {
                    $this->lastError = $data['error']['message'];
                }

                return [];
            }

            $data = json_decode($response, true);

            if (isset($data['webPages']['value']) && is_array($data['webPages']['value'])) {
                foreach ($data['webPages']['value'] as $item) {
                    $results[] = [
                        'url' => $item['url'] ?? '',
                        'title' => $item['name'] ?? '',
                        'snippet' => $item['snippet'] ?? '',
                        'source' => self::NAME,
                        'displayUrl' => $item['displayUrl'] ?? '',
                        'dateLastCrawled' => $item['dateLastCrawled'] ?? null,
                    ];
                }
            }

            // If more results needed and available
            if (count($results) < $maxResults) {
                $totalEstimated = $data['webPages']['totalEstimatedMatches'] ?? 0;
                if ($totalEstimated > $offset + $count && count($results) < $maxResults) {
                    $remaining = $maxResults - count($results);
                    $nextOffset = $offset + $count;
                    $nextResults = $this->search($query, $remaining, $nextOffset);
                    $results = array_merge($results, $nextResults);
                }
            }

        } catch (\Exception $e) {
            $this->lastError = $e->getMessage();
        }

        return array_slice($results, 0, $maxResults);
    }

    public function getLastError(): ?string
    {
        return $this->lastError;
    }

    /**
     * Set market/locale for search results
     */
    public function setMarket(string $market): self
    {
        $this->market = $market;
        return $this;
    }
}
