<?php

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

class SEO_Fury_Indexnow {

    private $table_name;

    public function __construct($table_name) {
        $this->table_name = $table_name;
        $this->init_hooks();
    }

    private function init_hooks() {
        add_action('wp_ajax_seo_submit_indexnow', array($this, 'ajax_submit_indexnow'));
        add_action('wp_ajax_seo_generate_indexnow_key', array($this, 'ajax_generate_indexnow_key'));
        add_action('wp_ajax_seo_verify_indexnow_key', array($this, 'ajax_verify_indexnow_key'));
        add_action('wp_ajax_seo_load_indexnow_history', array($this, 'ajax_load_indexnow_history'));
        add_action('wp_ajax_seo_clear_indexnow_history', array($this, 'ajax_clear_indexnow_history'));
        add_action('wp_ajax_seo_save_indexnow_settings', array($this, 'ajax_save_indexnow_settings'));
        add_action('wp_ajax_seo_indexnow_sitemap_urls', array($this, 'ajax_get_sitemap_urls'));

        add_action('save_post', array($this, 'auto_submit_to_indexnow'), 10, 3);
        add_action('delete_post', array($this, 'auto_submit_to_indexnow_on_delete'));

        add_action('init', array($this, 'serve_indexnow_key'));
    }

    public function register_settings() {
        $sanitize_checkbox = array('sanitize_callback' => 'absint');
        $sanitize_text     = array('sanitize_callback' => 'sanitize_text_field');

        register_setting('seo_fury_indexnow_group', 'seo_fury_indexnow_api_key', $sanitize_text);
        register_setting('seo_fury_indexnow_group', 'seo_fury_indexnow_search_engine', $sanitize_text);
        register_setting('seo_fury_indexnow_group', 'seo_fury_indexnow_auto_submit', $sanitize_checkbox);
        register_setting('seo_fury_indexnow_group', 'seo_fury_indexnow_auto_delete', $sanitize_checkbox);

        add_settings_section(
            'seo_fury_indexnow_section',
            __('IndexNow Settings', 'seo-fury'),
            array($this, 'indexnow_section_callback'),
            'seo_fury_indexnow_group'
        );

        add_settings_field(
            'seo_fury_indexnow_api_key',
            __('IndexNow API Key', 'seo-fury'),
            array($this, 'indexnow_api_key_callback'),
            'seo_fury_indexnow_group',
            'seo_fury_indexnow_section'
        );

        add_settings_field(
            'seo_fury_indexnow_search_engine',
            __('Search Engine', 'seo-fury'),
            array($this, 'indexnow_search_engine_callback'),
            'seo_fury_indexnow_group',
            'seo_fury_indexnow_section'
        );

        add_settings_field(
            'seo_fury_indexnow_auto_submit',
            __('Automatic Submission', 'seo-fury'),
            array($this, 'indexnow_auto_submit_callback'),
            'seo_fury_indexnow_group',
            'seo_fury_indexnow_section'
        );

        add_settings_field(
            'seo_fury_indexnow_auto_delete',
            __('Submit on Delete', 'seo-fury'),
            array($this, 'indexnow_auto_delete_callback'),
            'seo_fury_indexnow_group',
            'seo_fury_indexnow_section'
        );
    }

    public function indexnow_section_callback() {
        echo '<p>' . esc_html__('Instant indexing module settings.', 'seo-fury') . ' <a href="https://www.indexnow.org/" target="_blank">' . esc_html__('Learn more', 'seo-fury') . '</a>.</p>';
    }

    public function indexnow_api_key_callback() {
        $api_key = get_option('seo_fury_indexnow_api_key', '');
        ?>
        <input type="text" name="seo_fury_indexnow_api_key" value="<?php echo esc_attr($api_key); ?>" class="regular-text" readonly />
        <button type="button" class="button" id="generate-indexnow-key"><?php esc_html_e('Generate Key', 'seo-fury'); ?></button>
        <?php if (!empty($api_key)): ?>
            <button type="button" class="button" id="verify-indexnow-key"><?php esc_html_e('Verify Key', 'seo-fury'); ?></button>
        <?php endif; ?>
        <p class="description"><?php esc_html_e('API key for IndexNow. It will be saved in the site root.', 'seo-fury'); ?></p>
        <div id="indexnow-key-status" style="margin-top: 10px;"></div>
        <?php
    }

    public function indexnow_search_engine_callback() {
        $engine = get_option('seo_fury_indexnow_search_engine', 'api.indexnow.org');
        $engines = array(
            'api.indexnow.org' => __('IndexNow (all search engines)', 'seo-fury'),
            'www.bing.com' => __('Bing', 'seo-fury'),
            'yandex.com' => __('Yandex', 'seo-fury'),
            'search.seznam.cz' => __('Seznam', 'seo-fury'),
        );
        ?>
        <select name="seo_fury_indexnow_search_engine">
            <?php foreach ($engines as $value => $label): ?>
                <option value="<?php echo esc_attr($value); ?>" <?php selected($engine, $value); ?>>
                    <?php echo esc_html($label); ?>
                </option>
            <?php endforeach; ?>
        </select>
        <p class="description"><?php esc_html_e('Select the search engine to notify.', 'seo-fury'); ?></p>
        <?php
    }

    public function indexnow_auto_submit_callback() {
        $enabled = get_option('seo_fury_indexnow_auto_submit', 0);
        ?>
        <label>
            <input type="checkbox" name="seo_fury_indexnow_auto_submit" value="1" <?php checked($enabled, 1); ?> />
            <?php esc_html_e('Automatically submit URLs on publish/update', 'seo-fury'); ?>
        </label>
        <?php
    }

    public function indexnow_auto_delete_callback() {
        $enabled = get_option('seo_fury_indexnow_auto_delete', 0);
        ?>
        <label>
            <input type="checkbox" name="seo_fury_indexnow_auto_delete" value="1" <?php checked($enabled, 1); ?> />
            <?php esc_html_e('Automatically notify on deletion', 'seo-fury'); ?>
        </label>
        <?php
    }

    public function ajax_submit_indexnow() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        $urls = isset($_POST['urls']) ? sanitize_textarea_field($_POST['urls']) : '';

        if (empty($urls)) {
            wp_send_json_error(__('Please provide URLs to submit', 'seo-fury'));
        }

        $url_list = array_filter(array_map('trim', explode("\n", $urls)));

        if (empty($url_list)) {
            wp_send_json_error(__('No valid URLs provided', 'seo-fury'));
        }

        $results = array();
        foreach ($url_list as $url) {
            $result = $this->submit_to_indexnow($url, 'manual');
            $results[] = $result;
        }

        wp_send_json_success(array(
            'message' => __('Submission completed', 'seo-fury'),
            'results' => $results
        ));
    }

    public function ajax_generate_indexnow_key() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        $key = bin2hex(random_bytes(16));

        // Remove the previous key file so regenerating doesn't leave orphaned
        // valid-looking key files accumulating in the web root.
        $old_key = get_option('seo_fury_indexnow_api_key', '');
        if (!empty($old_key) && preg_match('/^[a-f0-9]{32}$/i', $old_key)) {
            $old_file = ABSPATH . $old_key . '.txt';
            if (file_exists($old_file)) {
                wp_delete_file($old_file);
            }
        }

        update_option('seo_fury_indexnow_api_key', $key);

        $key_file = ABSPATH . $key . '.txt';
        $file_created = file_put_contents($key_file, $key);

        if ($file_created === false) {
            wp_send_json_error(__('Failed to create key file. Check permissions.', 'seo-fury'));
        }

        wp_send_json_success(array(
            'key' => $key,
            'file' => $key . '.txt',
            'url' => home_url($key . '.txt')
        ));
    }

    public function ajax_verify_indexnow_key() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        $api_key = get_option('seo_fury_indexnow_api_key', '');

        if (empty($api_key)) {
            wp_send_json_error(__('API key is not configured', 'seo-fury'));
        }

        $key_file = ABSPATH . $api_key . '.txt';
        if (!file_exists($key_file)) {
            wp_send_json_error(__('Key file not found on the server', 'seo-fury'));
        }

        $file_content = file_get_contents($key_file);
        if ($file_content !== $api_key) {
            wp_send_json_error(__('Key file content does not match', 'seo-fury'));
        }

        $key_url = home_url($api_key . '.txt');
        $response = wp_remote_get($key_url);

        if (is_wp_error($response)) {
            /* translators: %s: error message from the HTTP request */
            wp_send_json_error(sprintf(__('Failed to check file availability: %s', 'seo-fury'), $response->get_error_message()));
        }

        $body = wp_remote_retrieve_body($response);
        $code = wp_remote_retrieve_response_code($response);

        if ($code !== 200) {
            /* translators: %d: HTTP response code */
            wp_send_json_error(sprintf(__('Key file is not accessible via URL (code: %d)', 'seo-fury'), $code));
        }

        if (trim($body) !== $api_key) {
            wp_send_json_error(__('Key file content via URL does not match', 'seo-fury'));
        }

        wp_send_json_success(array(
            'message' => __('Key verified successfully!', 'seo-fury'),
            'url' => $key_url
        ));
    }

    public function ajax_load_indexnow_history() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

        $history = $wpdb->get_results(
            "SELECT * FROM {$this->table_name} ORDER BY submitted_at DESC LIMIT 100"
        );

        wp_send_json_success($history);
    }

    public function ajax_clear_indexnow_history() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

        $result = $wpdb->query("TRUNCATE TABLE {$this->table_name}"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is set in constructor, not user input

        if ($result !== false) {
            wp_send_json_success(__('History cleared', 'seo-fury'));
        } else {
            wp_send_json_error(__('Failed to clear history', 'seo-fury'));
        }
    }

    public function ajax_save_indexnow_settings() {
        check_ajax_referer('seo_indexnow_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        $auto_submit = isset($_POST['auto_submit']) ? intval($_POST['auto_submit']) : 0;
        $auto_delete = isset($_POST['auto_delete']) ? intval($_POST['auto_delete']) : 0;
        $search_engine = isset($_POST['search_engine']) ? sanitize_text_field($_POST['search_engine']) : 'api.indexnow.org';

        update_option('seo_fury_indexnow_auto_submit', $auto_submit);
        update_option('seo_fury_indexnow_auto_delete', $auto_delete);
        update_option('seo_fury_indexnow_search_engine', $search_engine);

        wp_send_json_success(__('Settings saved', 'seo-fury'));
    }

/**
 * AJAX: return the list of indexable URLs (homepage + published posts of the
 * sitemap-enabled post types) so the browser can submit them to IndexNow in
 * batches using the existing submit flow.
 */
public function ajax_get_sitemap_urls() {
    check_ajax_referer('seo_indexnow_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
    }

    $post_types = get_option('seo_fury_sitemap_post_types', array('post', 'page'));
    if (!is_array($post_types) || empty($post_types)) {
        $post_types = array('post', 'page');
    }
    // Drop post types marked noindex in the sitemap settings.
    $post_types = array_values(array_filter($post_types, function ($pt) {
        return ! get_option('seo_fury_pt_robots_noindex_' . $pt, 0);
    }));

    $urls = array(home_url('/'));

    if (!empty($post_types)) {
        $q = new WP_Query(array(
            'post_type'              => $post_types,
            'post_status'            => 'publish',
            'posts_per_page'         => 2000,
            'fields'                 => 'ids',
            'no_found_rows'          => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
            'meta_query'             => array(
                'relation' => 'OR',
                array('key' => '_seo_fury_page_robots_noindex', 'compare' => 'NOT EXISTS'),
                array('key' => '_seo_fury_page_robots_noindex', 'value' => '1', 'compare' => '!='),
            ),
        ));
        foreach ($q->posts as $pid) {
            $permalink = get_permalink($pid);
            if ($permalink) {
                $urls[] = $permalink;
            }
        }
    }

    $urls = array_values(array_unique($urls));

    wp_send_json_success(array(
        'urls'  => $urls,
        'count' => count($urls),
    ));
}

public function auto_submit_to_indexnow($post_id, $post, $update) {
    $auto_submit = get_option('seo_fury_indexnow_auto_submit', 0);
    if (!$auto_submit) {
        return;
    }

    if ($post->post_status !== 'publish') {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (wp_is_post_revision($post_id)) {
        return;
    }

    if ($post->post_type === 'attachment') {
        return;
    }

    $excluded_post_types = array('revision', 'nav_menu_item', 'custom_css', 'customize_changeset', 'oembed_cache', 'user_request', 'wp_block');
    if (in_array($post->post_type, $excluded_post_types)) {
        return;
    }

    $url = get_permalink($post_id);

    if (!$url) {
        return;
    }

    if (strpos($url, '?p=') !== false || strpos($url, '?page_id=') !== false) {
        return;
    }

    if (strpos($url, '-zip') !== false || strpos($url, '.zip') !== false) {
        return;
    }

    // Debounce: a bulk edit, importer, or programmatic update can fire
    // save_post many times for the same post in quick succession. Each call
    // here makes a synchronous 30s remote POST, so skip URLs already submitted
    // in the last few minutes to avoid blocking the request and tripping the
    // search engine's rate limits.
    $debounce_key = 'seo_fury_inow_' . md5($url);
    if (get_transient($debounce_key)) {
        return;
    }
    set_transient($debounce_key, 1, 5 * MINUTE_IN_SECONDS);

    $this->submit_to_indexnow($url, 'auto_publish');
}

public function auto_submit_to_indexnow_on_delete($post_id) {
    $auto_delete = get_option('seo_fury_indexnow_auto_delete', 0);
    if (!$auto_delete) {
        return;
    }

    $post = get_post($post_id);
    if (!$post) {
        return;
    }

    if ($post->post_type === 'attachment') {
        return;
    }

    $excluded_post_types = array('revision', 'nav_menu_item', 'custom_css', 'customize_changeset', 'oembed_cache', 'user_request', 'wp_block');
    if (in_array($post->post_type, $excluded_post_types)) {
        return;
    }

    $url = get_permalink($post_id);

    if (!$url) {
        return;
    }

    if (strpos($url, '?p=') !== false || strpos($url, '?page_id=') !== false) {
        return;
    }

    if (strpos($url, '-zip') !== false || strpos($url, '.zip') !== false) {
        return;
    }

    $this->submit_to_indexnow($url, 'auto_delete');
}

    private function submit_to_indexnow($url, $method = 'manual') {
        global $wpdb;

        $api_key = get_option('seo_fury_indexnow_api_key', '');
        $search_engine = get_option('seo_fury_indexnow_search_engine', 'api.indexnow.org');

        if (empty($api_key)) {
            $this->log_indexnow_request($url, null, __('API key is not configured', 'seo-fury'), $method);
            return array('success' => false, 'message' => __('API key is not configured', 'seo-fury'));
        }

        $host = wp_parse_url(home_url(), PHP_URL_HOST);
        $key_location = home_url($api_key . '.txt');

        $body = array(
            'host' => $host,
            'key' => $api_key,
            'keyLocation' => $key_location,
            'urlList' => array($url)
        );

        $api_url = 'https://' . $search_engine . '/indexnow';

        $response = wp_remote_post($api_url, array(
            'headers' => array(
                'Content-Type' => 'application/json; charset=utf-8'
            ),
            'body' => json_encode($body),
            'timeout' => 30
        ));

        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            $this->log_indexnow_request($url, null, $error_message, $method);
            return array('success' => false, 'message' => $error_message);
        }

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

        $this->log_indexnow_request($url, $response_code, $response_body, $method);

        if (in_array($response_code, array(200, 202))) {
            return array('success' => true, 'message' => __('URL sent successfully', 'seo-fury'), 'code' => $response_code);
        } else {
            /* translators: %d: HTTP response code */
            return array('success' => false, 'message' => sprintf(__('Error: %d', 'seo-fury'), $response_code), 'code' => $response_code);
        }
    }

    private function log_indexnow_request($url, $response_code, $response_message, $method) {
        global $wpdb;

        $wpdb->insert(
            $this->table_name,
            array(
                'url' => $url,
                'response_code' => $response_code,
                'response_message' => $response_message,
                'submitted_at' => current_time('mysql'),
                'method' => $method
            ),
            array('%s', '%d', '%s', '%s', '%s')
        );
    }

public function serve_indexnow_key() {
    if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
        return;
    }

    $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
    $parsed_uri = wp_parse_url($request_uri, PHP_URL_PATH);

    // Match the key file as the final path segment, allowing a subdirectory
    // prefix (e.g. /blog/{key}.txt on subdir installs) — the original ^/ anchor
    // only matched root installs.
    if (preg_match('#/([a-f0-9]{32})\.txt$#i', (string) $parsed_uri, $matches)) {
        $requested_key = $matches[1];
        $stored_key = get_option('seo_fury_indexnow_api_key', '');

        if (!empty($stored_key) && hash_equals($stored_key, $requested_key)) {
            header('Content-Type: text/plain; charset=utf-8');
            echo $stored_key; // 32 hex chars, no escaping needed for a plain-text body
            exit;
        }
    }
}
}
