<?php

/**
 * Robots & Indexing REST API Controller
 *
 * @package ProRank\SEO\Core\RestApi
 * @since   0.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\RestApi;

use ProRank\SEO\Core\Security\Sanitization;
use ProRank\SEO\Core\Security\Validation;
use ProRank\SEO\Core\Config\Settings;
use ProRank\SEO\Services\RobotsTxtValidator;
use WP_REST_Controller;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

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


/**
 * Class RobotsIndexingController
 * 
 * Handles REST API endpoints for robots & indexing settings
 */


class RobotsIndexingController extends WP_REST_Controller {
    /**
     * Namespace
     *
     * @var string
     */
    protected $namespace = 'prorank-seo/v1';

    /**
     * Rest base
     *
     * @var string
     */
    protected $rest_base = 'settings/robots_indexing';

    /**
     * Register routes
     *
     * @return void
     */
    public function register_routes(): void {
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'get_settings'],
                'permission_callback' => [$this, 'get_settings_permissions_check'],
            ],
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [$this, 'update_settings'],
                'permission_callback' => [$this, 'update_settings_permissions_check'],
                'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
            ],
            'schema' => [$this, 'get_public_item_schema'],
        ]);

        // Check physical robots.txt
        register_rest_route($this->namespace, '/' . $this->rest_base . '/check-robots-file', [
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [$this, 'check_robots_file'],
            'permission_callback' => [$this, 'get_settings_permissions_check'],
        ]);

        // Validate API keys
        register_rest_route($this->namespace, '/' . $this->rest_base . '/validate-key', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'validate_api_key'],
            'permission_callback' => [$this, 'update_settings_permissions_check'],
            'args'                => [
                'type' => [
                    'description'       => __('Type of key to validate', 'prorank-seo'),
                    'type'              => 'string',
                    'enum'              => ['indexnow'],
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'key' => [
                    'description'       => __('The key to validate', 'prorank-seo'),
                    'type'              => 'string',
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_textarea_field',
                ],
            ],
        ]);

        // Generate IndexNow API key
        register_rest_route($this->namespace, '/' . $this->rest_base . '/generate-indexnow-key', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'generate_indexnow_key'],
            'permission_callback' => [$this, 'update_settings_permissions_check'],
        ]);

        // Validate IndexNow key file accessibility
        register_rest_route($this->namespace, '/' . $this->rest_base . '/validate-key-file', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'validate_indexnow_key_file'],
            'permission_callback' => [$this, 'update_settings_permissions_check'],
            'args'                => [
                'key' => [
                    'description'       => __('IndexNow key to verify against key file (optional, uses saved key if omitted)', 'prorank-seo'),
                    'type'              => 'string',
                    'required'          => false,
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);
        
        // Validate robots.txt syntax
        register_rest_route($this->namespace, '/' . $this->rest_base . '/validate-robots', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'validate_robots_txt'],
            'permission_callback' => [$this, 'update_settings_permissions_check'],
            'args'                => [
                'content' => [
                    'description'       => __('Robots.txt content to validate', 'prorank-seo'),
                    'type'              => 'string',
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_textarea_field',
                ],
            ],
        ]);
        
        // Test URL against robots.txt
        register_rest_route($this->namespace, '/' . $this->rest_base . '/test-url', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'test_robots_url'],
            'permission_callback' => [$this, 'get_settings_permissions_check'],
            'args'                => [
                'content' => [
                    'description'       => __('Robots.txt content', 'prorank-seo'),
                    'type'              => 'string',
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_textarea_field',
                ],
                'url' => [
                    'description'       => __('URL to test', 'prorank-seo'),
                    'type'              => 'string',
                    'required'          => true,
                    'sanitize_callback' => 'esc_url_raw',
                ],
                'user_agent' => [
                    'description'       => __('User agent to test as', 'prorank-seo'),
                    'type'              => 'string',
                    'default'           => '*',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);

        // 2025 Enhancement: Submit to all IndexNow engines
        register_rest_route($this->namespace, '/' . $this->rest_base . '/submit-to-all-engines', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'submit_to_all_engines'],
            'permission_callback' => [$this, 'update_settings_permissions_check'],
            'args'                => [
                'urls' => [
                    'description'       => __('URLs to submit (array)', 'prorank-seo'),
                    'type'              => 'array',
                    'required'          => true,
                    'items'             => [
                        'type' => 'string',
                    ],
                ],
            ],
        ]);

        // 2025 Enhancement: Get IndexNow submission log
        register_rest_route($this->namespace, '/' . $this->rest_base . '/indexnow-log', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'get_indexnow_log'],
                'permission_callback' => [$this, 'get_settings_permissions_check'],
                'args'                => [
                    'limit' => [
                        'description'       => __('Number of entries to return', 'prorank-seo'),
                        'type'              => 'integer',
                        'default'           => 20,
                        'minimum'           => 1,
                        'maximum'           => 100,
                        'sanitize_callback' => 'absint',
                    ],
                ],
            ],
            [
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => [$this, 'clear_indexnow_log'],
                'permission_callback' => [$this, 'update_settings_permissions_check'],
            ],
        ]);
    }

    /**
     * Get settings
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function get_settings($request) {
        $settings = Settings::get('robots_indexing', [
            'enable_robots_editor' => false,
            'robots_txt_rules' => '',
            'block_ai_bots' => false,
            'enable_indexnow' => false,
            'indexnow_api_key' => '',
            // Content Safeguard settings
            'enable_noai_meta' => false,
            'enable_noimageai_meta' => false,
            'enable_rule_noindex' => false,
            'rule_min_words' => 300,
            'rule_max_age_days' => 0,
            // Global Noindex settings
            'noindex_posts' => false,
            'noindex_pages' => false,
            'noindex_attachments' => true,
            'noindex_author' => false,
            'noindex_date' => true,
            'noindex_category' => false,
            'noindex_tag' => false,
            'noindex_search' => true,
            'noindex_404' => true,
            'noindex_paginated' => false,
        ]);

        return new WP_REST_Response([
            'settings' => $settings,
            'meta' => [
                'is_core' => false,
                'physical_robots_exists' => file_exists(ABSPATH . 'robots.txt'),
            ],
        ], 200);
    }

    /**
     * Update settings
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function update_settings($request) {
        $settings = [];

        // Compatibility: some admin screens send payload under { settings: {...} }.
        // Normalize by promoting nested values to top-level request params.
        $nested_settings = $request->get_param('settings');
        if (is_array($nested_settings)) {
            foreach ($nested_settings as $key => $value) {
                if (!$request->has_param($key)) {
                    $request->set_param((string) $key, $value);
                }
            }
        }

        // Robots.txt editor
        if ($request->has_param('enable_robots_editor')) {
            $settings['enable_robots_editor'] = (bool) $request->get_param('enable_robots_editor');
        }

        if ($request->has_param('robots_txt_rules')) {
            $settings['robots_txt_rules'] = sanitize_textarea_field($request->get_param('robots_txt_rules'));
        }

        if ($request->has_param('block_ai_bots')) {
            $settings['block_ai_bots'] = (bool) $request->get_param('block_ai_bots');
        }

        // IndexNow
        if ($request->has_param('enable_indexnow')) {
            $settings['enable_indexnow'] = (bool) $request->get_param('enable_indexnow');
        }

        if ($request->has_param('indexnow_api_key')) {
            $settings['indexnow_api_key'] = sanitize_text_field($request->get_param('indexnow_api_key'));
        }

        // Content Safeguard settings
        if ($request->has_param('enable_noai_meta')) {
            $settings['enable_noai_meta'] = (bool) $request->get_param('enable_noai_meta');
        }

        if ($request->has_param('enable_noimageai_meta')) {
            $settings['enable_noimageai_meta'] = (bool) $request->get_param('enable_noimageai_meta');
        }

        if ($request->has_param('enable_rule_noindex')) {
            $settings['enable_rule_noindex'] = (bool) $request->get_param('enable_rule_noindex');
        }

        if ($request->has_param('rule_min_words')) {
            $settings['rule_min_words'] = (int) $request->get_param('rule_min_words');
        }

        if ($request->has_param('rule_max_age_days')) {
            $settings['rule_max_age_days'] = (int) $request->get_param('rule_max_age_days');
        }

        // Global Noindex settings
        $noindex_fields = [
            'noindex_posts', 'noindex_pages', 'noindex_attachments',
            'noindex_author', 'noindex_date', 'noindex_category',
            'noindex_tag', 'noindex_search', 'noindex_404', 'noindex_paginated'
        ];

        foreach ($noindex_fields as $field) {
            if ($request->has_param($field)) {
                $settings[$field] = (bool) $request->get_param($field);
            }
        }

        // Get existing settings to merge
        $existing = Settings::get('robots_indexing', []);
        $settings = array_merge($existing, $settings);

        // Save settings
        $result = Settings::update('robots_indexing', $settings);

        if (!$result) {
            return new WP_Error(
                'prorank_settings_save_failed',
                __('Failed to save settings.', 'prorank-seo'),
                ['status' => 500]
            );
        }

        // Create IndexNow key file if needed
        if (!empty($settings['enable_indexnow']) && !empty($settings['indexnow_api_key'])) {
            $this->create_indexnow_key_file($settings['indexnow_api_key']);
        }

        return new WP_REST_Response([
            'success' => true,
            'settings' => $settings,
        ], 200);
    }

    /**
     * Check if physical robots.txt exists
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function check_robots_file($request) {
        $robots_path = ABSPATH . 'robots.txt';
        $exists = file_exists($robots_path);
        
        $response = [
            'exists' => $exists,
        ];
        
        if ($exists) {
            $response['content'] = @file_get_contents($robots_path);
            $response['message'] = __('A physical robots.txt file exists. The virtual editor will be disabled.', 'prorank-seo');
        }

        return new WP_REST_Response($response, 200);
    }

    /**
     * Validate API key
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function validate_api_key($request) {
        $type = $request->get_param('type');
        $key = $request->get_param('key');

        if ($type === 'indexnow') {
            // Basic validation for IndexNow key format
            if (strlen($key) < 16 || strlen($key) > 128) {
                return new WP_Error(
                    'prorank_invalid_key',
                    __('IndexNow key must be between 16 and 128 characters.', 'prorank-seo'),
                    ['status' => 400]
                );
            }

            return new WP_REST_Response([
                'valid' => true,
                'message' => __('IndexNow key format is valid.', 'prorank-seo'),
            ], 200);
        }

        return new WP_Error(
            'prorank_invalid_type',
            __('Invalid key type.', 'prorank-seo'),
            ['status' => 400]
        );
    }

    /**
     * Generate and persist a new IndexNow API key.
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function generate_indexnow_key($request) {
        try {
            $key = bin2hex(random_bytes(16));
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_key_generation_failed',
                __('Failed to generate a secure IndexNow key.', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $settings = Settings::get('robots_indexing', []);
        $settings['indexnow_api_key'] = $key;
        $settings['enable_indexnow'] = true;

        $result = Settings::update('robots_indexing', $settings);
        if (!$result) {
            return new WP_Error(
                'prorank_settings_save_failed',
                __('Failed to save IndexNow settings.', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $key_endpoint_ready = $this->create_indexnow_key_file($key);
        if (!$key_endpoint_ready) {
            return new WP_Error(
                'prorank_key_file_creation_failed',
                __('Key was generated but the verification endpoint could not be prepared.', 'prorank-seo'),
                ['status' => 500]
            );
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => __('IndexNow key generated. The verification endpoint is now available virtually.', 'prorank-seo'),
            'key' => $key,
            'key_file_url' => trailingslashit(home_url('/')) . rawurlencode($key) . '.txt',
            'settings' => $settings,
        ], 200);
    }

    /**
     * Validate that IndexNow key file is publicly reachable and matches key content.
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function validate_indexnow_key_file($request) {
        $settings = Settings::get('robots_indexing', []);
        $key = sanitize_text_field((string) $request->get_param('key'));
        if ($key === '') {
            $key = sanitize_text_field((string) ($settings['indexnow_api_key'] ?? ''));
        }

        if ($key === '') {
            return new WP_Error(
                'prorank_indexnow_no_key',
                __('IndexNow API key is not configured.', 'prorank-seo'),
                ['status' => 400]
            );
        }

        $indexnow = new \ProRank\SEO\Services\IndexNowService($key);
        $is_valid = $indexnow->verify_key_file();
        $key_file_url = trailingslashit(home_url('/')) . rawurlencode($key) . '.txt';

        if (!$is_valid) {
            return new WP_Error(
                'prorank_indexnow_key_file_invalid',
                $indexnow->get_last_error() ?: __('IndexNow key file validation failed.', 'prorank-seo'),
                [
                    'status' => 400,
                    'key_file_url' => $key_file_url,
                ]
            );
        }

        return new WP_REST_Response([
            'valid' => true,
            'message' => __('IndexNow key file is reachable and matches the key.', 'prorank-seo'),
            'key_file_url' => $key_file_url,
        ], 200);
    }

    /**
     * Prepare the virtual IndexNow key endpoint.
     *
     * The free plugin does not write verification files to the site root.
     * Requests to `/{key}.txt` are served virtually by the frontend layer.
     *
     * @param string $key IndexNow API key.
     * @return bool
     */
    private function create_indexnow_key_file(string $key): bool {
        return $key !== '';
    }

    /**
     * Check if a given request has access to get settings
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
     */
    public function get_settings_permissions_check($request) {
        return current_user_can('manage_options');
    }

    /**
     * Check if a given request has access to update settings
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return true|WP_Error True if the request has access to update settings, WP_Error object otherwise.
     */
    public function update_settings_permissions_check($request) {
        return current_user_can('manage_options');
    }

    /**
     * Validate robots.txt syntax
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function validate_robots_txt($request) {
        $content = $request->get_param('content');
        
        $validator = new RobotsTxtValidator();
        $is_valid = $validator->validate($content);
        
        return new WP_REST_Response([
            'valid' => $is_valid,
            'errors' => $validator->get_errors(),
            'warnings' => $validator->get_warnings(),
            'preview' => $validator->generate_preview($content),
        ], 200);
    }
    
    /**
     * Test URL against robots.txt rules
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function test_robots_url($request) {
        $content = $request->get_param('content');
        $url = $request->get_param('url');
        $user_agent = $request->get_param('user_agent') ?: '*';
        
        $validator = new RobotsTxtValidator();
        $result = $validator->test_url($content, $url, $user_agent);
        
        return new WP_REST_Response($result, 200);
    }
    
    /**
     * Submit URLs to all IndexNow engines
     *
     * 2025 Enhancement: Endpoint to submit URLs to all 4 search engines
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function submit_to_all_engines($request) {
        $urls = $request->get_param('urls');
        $settings = Settings::get('robots_indexing', []);
        $api_key = $settings['indexnow_api_key'] ?? '';

        if (empty($api_key)) {
            return new WP_Error(
                'prorank_indexnow_no_key',
                __('IndexNow API key is not configured', 'prorank-seo'),
                ['status' => 400]
            );
        }

        $indexnow = new \ProRank\SEO\Services\IndexNowService($api_key);
        $results = $indexnow->submit_to_all($urls);

        $success_count = count(array_filter($results));
        $total_count = count($results);

        return new WP_REST_Response([
            'success' => $success_count > 0,
            'message' => sprintf(
                /* translators: %1$d: successful submissions, %2$d: total engines */
                __('Submitted to %1$d out of %2$d search engines', 'prorank-seo'),
                $success_count,
                $total_count
            ),
            'results' => $results,
            'urls_submitted' => count($urls),
        ], 200);
    }

    /**
     * Get IndexNow submission log
     *
     * 2025 Enhancement: Retrieve submission history
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function get_indexnow_log($request) {
        $limit = $request->get_param('limit') ?: 20;
        $settings = Settings::get('robots_indexing', []);
        $api_key = $settings['indexnow_api_key'] ?? '';

        if (empty($api_key)) {
            return new WP_Error(
                'prorank_indexnow_no_key',
                __('IndexNow API key is not configured', 'prorank-seo'),
                ['status' => 400]
            );
        }

        $indexnow = new \ProRank\SEO\Services\IndexNowService($api_key);
        $log = $indexnow->get_recent_log($limit);

        return new WP_REST_Response([
            'log' => $log,
            'count' => count($log),
        ], 200);
    }

    /**
     * Clear IndexNow submission log
     *
     * 2025 Enhancement: Clear submission history
     *
     * @param WP_REST_Request $request Full details about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function clear_indexnow_log($request) {
        $settings = Settings::get('robots_indexing', []);
        $api_key = $settings['indexnow_api_key'] ?? '';

        if (empty($api_key)) {
            return new WP_Error(
                'prorank_indexnow_no_key',
                __('IndexNow API key is not configured', 'prorank-seo'),
                ['status' => 400]
            );
        }

        $indexnow = new \ProRank\SEO\Services\IndexNowService($api_key);
        $indexnow->clear_log();

        return new WP_REST_Response([
            'success' => true,
            'message' => __('IndexNow submission log cleared', 'prorank-seo'),
        ], 200);
    }

    /**
     * Get schema for settings
     *
     * @return array
     */
    public function get_item_schema(): array {
        if ($this->schema) {
            return $this->add_additional_fields_schema($this->schema);
        }

        $schema = [
            '$schema'    => 'http://json-schema.org/draft-04/schema#',
            'title'      => 'robots-indexing-settings',
            'type'       => 'object',
            'properties' => [
                'enable_robots_editor' => [
                    'description' => __('Enable virtual robots.txt editor.', 'prorank-seo'),
                    'type'        => 'boolean',
                    'context'     => ['view', 'edit'],
                    'default'     => false,
                ],
                'robots_txt_rules' => [
                    'description' => __('Custom robots.txt rules.', 'prorank-seo'),
                    'type'        => 'string',
                    'context'     => ['view', 'edit'],
                    'default'     => '',
                ],
                'enable_indexnow' => [
                    'description' => __('Enable IndexNow API.', 'prorank-seo'),
                    'type'        => 'boolean',
                    'context'     => ['view', 'edit'],
                    'default'     => false,
                ],
                'indexnow_api_key' => [
                    'description' => __('IndexNow API key.', 'prorank-seo'),
                    'type'        => 'string',
                    'context'     => ['view', 'edit'],
                    'default'     => '',
                ],
            ],
        ];

        $this->schema = $schema;

        return $this->add_additional_fields_schema($this->schema);
    }
}
