<?php

/**
 * IndexNow API Service
 *
 * @package ProRank\SEO\Services
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Services;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


/**
 * Class IndexNowService
 *
 * Handles IndexNow API submissions for instant indexing
 */


class IndexNowService {
    /**
     * IndexNow endpoints for different search engines
     *
     * @var array
     */
    private const ENDPOINTS = [
        'bing' => 'https://www.bing.com/indexnow',
        'yandex' => 'https://yandex.com/indexnow',
        'seznam' => 'https://search.seznam.cz/indexnow',
        'naver' => 'https://searchadvisor.naver.com/indexnow',
    ];

    /**
     * API key
     *
     * @var string|null
     */
    private ?string $api_key = null;

    /**
     * Last error message
     *
     * @var string|null
     */
    private ?string $last_error = null;

    /**
     * Constructor
     *
     * @param string|null $api_key IndexNow API key
     */
    public function __construct(?string $api_key = null) {
        $this->api_key = $api_key;
    }

    /**
     * Set API key
     *
     * @param string $api_key API key
     * @return void
     */
    public function set_api_key(string $api_key): void {
        $this->api_key = $api_key;
    }

    /**
     * Submit single URL to IndexNow
     *
     * @param string $url URL to submit
     * @param string $endpoint Which endpoint to use (default: bing)
     * @return bool Success status
     */
    public function submit_url(string $url, string $endpoint = 'bing'): bool {
        return $this->submit_urls([$url], $endpoint);
    }

    /**
     * Submit multiple URLs to IndexNow (batch submission)
     *
     * @param array  $urls URLs to submit (max 10,000 per request)
     * @param string $endpoint Which endpoint to use (default: bing)
     * @return bool Success status
     */
    public function submit_urls(array $urls, string $endpoint = 'bing'): bool {
        $this->last_error = null;

        // Validate API key
        if (empty($this->api_key)) {
            $this->last_error = __('IndexNow API key is not configured', 'prorank-seo');
            return false;
        }

        // Validate endpoint
        if (!isset(self::ENDPOINTS[$endpoint])) {
            $this->last_error = sprintf(
/* translators: %1$: value */
                __('Invalid IndexNow endpoint: %1$s', 'prorank-seo'),
                $endpoint
            );
            return false;
        }

        // Validate URLs
        if (empty($urls)) {
            $this->last_error = __('No URLs provided for submission', 'prorank-seo');
            return false;
        }

        // IndexNow supports up to 10,000 URLs per request
        if (count($urls) > 10000) {
            $urls = array_slice($urls, 0, 10000);
        }

        // Prepare request body
        $host = wp_parse_url(home_url(), PHP_URL_HOST);
        $key_location = home_url('/' . $this->api_key . '.txt');

        $body = [
            'host' => $host,
            'key' => $this->api_key,
            'keyLocation' => $key_location,
        ];

        // Bing's POST endpoint requires urlList (array) for all submissions
        $body['urlList'] = array_values($urls);

        // Send request
        $response = wp_remote_post(self::ENDPOINTS[$endpoint], [
            'body' => wp_json_encode($body),
            'headers' => [
                'Content-Type' => 'application/json; charset=utf-8',
            ],
            'timeout' => 30,
            'user-agent' => 'ProRank SEO/' . PRORANK_SEO_VERSION,
        ]);

        // Handle response
        if (is_wp_error($response)) {
            $this->last_error = sprintf(
/* translators: %1$: value */
                __('IndexNow submission failed: %1$s', 'prorank-seo'),
                $response->get_error_message()
            );
            return false;
        }

        $response_code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);

        // Success codes: 200, 202
        if (in_array($response_code, [200, 202], true)) {
            // Log successful submission
            $this->log_submission($urls, $endpoint, true);
            return true;
        }

        // Handle specific error codes
        switch ($response_code) {
            case 400:
                $this->last_error = __('Bad request - Invalid format', 'prorank-seo');
                break;
            case 403:
                $this->last_error = __('Forbidden - Invalid API key or key file not accessible', 'prorank-seo');
                break;
            case 422:
                $this->last_error = __('Unprocessable Entity - URLs don\'t belong to the host', 'prorank-seo');
                break;
            case 429:
                $this->last_error = __('Too Many Requests - Rate limit exceeded', 'prorank-seo');
                break;
            default:
                $this->last_error = sprintf(
/* translators: %1$: value, %2$: value */
                    __('IndexNow submission failed with HTTP %1$d: %2$s', 'prorank-seo'),
                    $response_code,
                    $response_body ?: __('Unknown error', 'prorank-seo')
                );
        }

        // Log failed submission
        $this->log_submission($urls, $endpoint, false, $this->last_error);

        return false;
    }

    /**
     * Ensure the virtual IndexNow key endpoint can be served.
     *
     * The free plugin serves `/{key}.txt` virtually via template_redirect
     * instead of writing physical files to the site root.
     *
     * @return bool Success status
     */
    public function create_key_file(): bool {
        if (empty($this->api_key)) {
            $this->last_error = __('API key is not set', 'prorank-seo');
            return false;
        }

        return true;
    }

    /**
     * Verify key file is accessible
     *
     * @return bool
     */
    public function verify_key_file(): bool {
        if (empty($this->api_key)) {
            $this->last_error = __('API key is not set', 'prorank-seo');
            return false;
        }

        $url = home_url('/' . $this->api_key . '.txt');
        
        $response = wp_remote_get($url, [
            'timeout' => 10,
            'sslverify' => true, // Allow self-signed certificates for local development
        ]);

        if (is_wp_error($response)) {
            $this->last_error = sprintf(
/* translators: %1$: value */
                __('Key file not accessible: %1$s', 'prorank-seo'),
                $response->get_error_message()
            );
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        
        if (trim($body) !== $this->api_key) {
            $this->last_error = __('Key file content does not match API key', 'prorank-seo');
            return false;
        }

        return true;
    }

    /**
     * Submit all search engines at once
     *
     * @param array $urls URLs to submit
     * @return array Results per endpoint
     */
    public function submit_to_all(array $urls): array {
        $results = [];

        foreach (array_keys(self::ENDPOINTS) as $endpoint) {
            $results[$endpoint] = $this->submit_urls($urls, $endpoint);
        }

        return $results;
    }

    /**
     * Get last error message
     *
     * @return string|null
     */
    public function get_last_error(): ?string {
        return $this->last_error;
    }

    /**
     * Log submission for debugging/analytics
     *
     * @param array  $urls URLs submitted
     * @param string $endpoint Endpoint used
     * @param bool   $success Success status
     * @param string|null $error Error message if failed
     * @return void
     */
    private function log_submission(array $urls, string $endpoint, bool $success, ?string $error = null): void {
        // Store in transient for recent activity display
        $log_key = 'prorank_indexnow_log';
        $log = get_transient($log_key) ?: [];

        $log[] = [
            'timestamp' => time(),
            'urls' => $urls,
            'endpoint' => $endpoint,
            'success' => $success,
            'error' => $error,
        ];

        // Keep only last 100 entries
        if (count($log) > 100) {
            $log = array_slice($log, -100);
        }

        set_transient($log_key, $log, DAY_IN_SECONDS);

        // Also log to error log if debug is enabled
        if (defined('WP_DEBUG') && WP_DEBUG) {
            prorank_log(sprintf(
                '[ProRank SEO IndexNow] %s submission to %s for %d URL(s): %s',
                $success ? 'Successful' : 'Failed',
                $endpoint,
                count($urls),
                $error ?: 'OK'
            ));
        }
    }

    /**
     * Get recent submission log
     *
     * @param int $limit Number of entries to return
     * @return array
     */
    public function get_recent_log(int $limit = 20): array {
        $log = get_transient('prorank_indexnow_log') ?: [];
        
        // Sort by timestamp descending
        usort($log, function($a, $b) {
            return $b['timestamp'] - $a['timestamp'];
        });

        return array_slice($log, 0, $limit);
    }

    /**
     * Clear submission log
     *
     * @return void
     */
    public function clear_log(): void {
        delete_transient('prorank_indexnow_log');
    }

    /**
     * Get supported endpoints
     *
     * @return array
     */
    public static function get_endpoints(): array {
        return self::ENDPOINTS;
    }
}
