<?php

/**
 * Redirects 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\Modules\TechnicalSEO\RedirectsModule;
use WP_REST_Controller;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

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


/**
 * Class RedirectsController
 * 
 * Handles REST API endpoints for redirect management
 */


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

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

    /**
     * Redirects module instance
     *
     * @var RedirectsModule|null
     */
    private ?RedirectsModule $redirects_module = null;

    /**
     * Constructor
     *
     * @param RedirectsModule|null $redirects_module Redirects module instance
     */
    public function __construct(?RedirectsModule $redirects_module = null) {
        $this->redirects_module = $redirects_module;
    }
    
    /**
     * Get redirects module instance
     *
     * @return RedirectsModule|null
     */
    private function get_redirects_module(): ?RedirectsModule {
        if (!$this->redirects_module && function_exists('prorank')) {
            // Try to get the module from the module manager
            $module_manager = prorank()->modules();
            if ($module_manager && $module_manager->is_active('redirects')) {
                $this->redirects_module = $module_manager->get_module('redirects');
            }
        }
        
        return $this->redirects_module;
    }

    /**
     * Register routes
     *
     * @return void
     */
    public function register_routes(): void {
        // List redirects
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'get_items'],
                'permission_callback' => [$this, 'get_items_permissions_check'],
                'args'                => $this->get_collection_params(),
            ],
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [$this, 'create_item'],
                'permission_callback' => [$this, 'create_item_permissions_check'],
                'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
            ],
            'schema' => [$this, 'get_public_item_schema'],
        ]);

        // Single redirect
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'get_item'],
                'permission_callback' => [$this, 'get_item_permissions_check'],
                'args'                => [
                    'id' => [
                        'description'       => __('Unique identifier for the redirect.', 'prorank-seo'),
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                    ],
                ],
            ],
            [
                'methods'             => WP_REST_Server::EDITABLE,
                'callback'            => [$this, 'update_item'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
                'args'                => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
            ],
            [
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => [$this, 'delete_item'],
                'permission_callback' => [$this, 'delete_item_permissions_check'],
                'args'                => [
                    'id' => [
                        'description'       => __('Unique identifier for the redirect.', 'prorank-seo'),
                        'type'              => 'integer',
                        'sanitize_callback' => 'absint',
                    ],
                ],
            ],
            'schema' => [$this, 'get_public_item_schema'],
        ]);

        // Bulk actions
        register_rest_route($this->namespace, '/' . $this->rest_base . '/bulk', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'bulk_action'],
            'permission_callback' => [$this, 'bulk_action_permissions_check'],
            'args'                => [
                'action' => [
                    'description'       => __('Bulk action to perform.', 'prorank-seo'),
                    'type'              => 'string',
                    'enum'              => ['activate', 'deactivate', 'delete'],
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'ids' => [
                    'description'       => __('Array of redirect IDs.', 'prorank-seo'),
                    'type'              => 'array',
                    'items'             => [
                        'type' => 'integer',
                    ],
                    'required'          => true,
                    'sanitize_callback' => [$this, 'sanitize_id_array'],
                ],
            ],
        ]);
    }

    /**
     * Get items
     *
     * @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_items($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        // Pass request directly to module - it handles its own parameter extraction
        return $module->get_redirects($request);
    }

    /**
     * Get single item
     *
     * @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_item($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        return $module->get_redirect($request);
    }

    /**
     * Create item
     *
     * @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 create_item($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        return $module->create_redirect($request);
    }

    /**
     * Update item
     *
     * @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_item($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        return $module->update_redirect($request);
    }

    /**
     * Delete item
     *
     * @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 delete_item($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        return $module->delete_redirect($request);
    }

    /**
     * Bulk action
     *
     * @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 bulk_action($request) {
        $module = $this->get_redirects_module();
        if (!$module) {
            return new WP_Error('no_module', __('Redirects module is not available', 'prorank-seo'), ['status' => 500]);
        }

        return $module->bulk_action($request);
    }

    /**
     * Check if a given request has access to get items
     *
     * @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_items_permissions_check($request) {
        return current_user_can('manage_options');
    }

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

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

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

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

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

    /**
     * Get the query params for collections
     *
     * @return array
     */
    public function get_collection_params(): array {
        return [
            'page' => [
                'description'       => __('Current page of the collection.', 'prorank-seo'),
                'type'              => 'integer',
                'default'           => 1,
                'sanitize_callback' => 'absint',
            ],
            'per_page' => [
                'description'       => __('Maximum number of items to be returned in result set.', 'prorank-seo'),
                'type'              => 'integer',
                'default'           => 20,
                'minimum'           => 1,
                'maximum'           => 100,
                'sanitize_callback' => 'absint',
            ],
            'search' => [
                'description'       => __('Limit results to those matching a string.', 'prorank-seo'),
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'type' => [
                'description'       => __('Limit results to specific redirect type.', 'prorank-seo'),
                'type'              => 'string',
                'enum'              => ['all', '301', '302', '307', '308'],
                'default'           => 'all',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'status' => [
                'description'       => __('Limit results to specific status.', 'prorank-seo'),
                'type'              => 'string',
                'enum'              => ['all', 'active', 'inactive'],
                'default'           => 'all',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'orderby' => [
                'description'       => __('Sort collection by attribute.', 'prorank-seo'),
                'type'              => 'string',
                'enum'              => ['id', 'source_url', 'type', 'status', 'hits', 'last_hit_timestamp'],
                'default'           => 'id',
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'order' => [
                'description'       => __('Order sort attribute ascending or descending.', 'prorank-seo'),
                'type'              => 'string',
                'enum'              => ['ASC', 'DESC'],
                'default'           => 'DESC',
                'sanitize_callback' => 'sanitize_text_field',
            ],
        ];
    }

    /**
     * Get schema for item
     *
     * @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'      => 'redirect',
            'type'       => 'object',
            'properties' => [
                'id' => [
                    'description' => __('Unique identifier for the redirect.', 'prorank-seo'),
                    'type'        => 'integer',
                    'context'     => ['view', 'edit'],
                    'readonly'    => true,
                ],
                'source_url' => [
                    'description' => __('The URL to redirect from.', 'prorank-seo'),
                    'type'        => 'string',
                    'context'     => ['view', 'edit'],
                    'required'    => true,
                    'arg_options' => [
                        'sanitize_callback' => 'sanitize_text_field',
                        'validate_callback' => [$this, 'validate_source_url'],
                    ],
                ],
                'target_url' => [
                    'description' => __('The URL to redirect to.', 'prorank-seo'),
                    'type'        => 'string',
                    'context'     => ['view', 'edit'],
                    'required'    => true,
                    'arg_options' => [
                        'sanitize_callback' => 'esc_url_raw',
                        'validate_callback' => [$this, 'validate_target_url'],
                    ],
                ],
                'type' => [
                    'description' => __('The redirect type.', 'prorank-seo'),
                    'type'        => 'integer',
                    'enum'        => [301, 302, 307, 308],
                    'context'     => ['view', 'edit'],
                    'default'     => 301,
                ],
                'status' => [
                    'description' => __('The redirect status.', 'prorank-seo'),
                    'type'        => 'string',
                    'enum'        => ['active', 'inactive'],
                    'context'     => ['view', 'edit'],
                    'default'     => 'active',
                ],
                'is_regex' => [
                    'description' => __('Whether the source URL is a regular expression.', 'prorank-seo'),
                    'type'        => 'boolean',
                    'context'     => ['view', 'edit'],
                    'default'     => false,
                ],
                'hits' => [
                    'description' => __('Number of times the redirect has been used.', 'prorank-seo'),
                    'type'        => 'integer',
                    'context'     => ['view'],
                    'readonly'    => true,
                ],
                'last_hit_timestamp' => [
                    'description' => __('Timestamp of the last redirect usage.', 'prorank-seo'),
                    'type'        => 'string',
                    'format'      => 'date-time',
                    'context'     => ['view'],
                    'readonly'    => true,
                ],
            ],
        ];

        $this->schema = $schema;

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

    /**
     * Validate source URL
     *
     * @param string          $value   Value to validate.
     * @param WP_REST_Request $request Current request object.
     * @param string          $param   Parameter name.
     * @return true|WP_Error
     */
    public function validate_source_url($value, $request, $param) {
        if (empty($value)) {
            return new WP_Error(
                'rest_invalid_param',
                __('Source URL cannot be empty.', 'prorank-seo'),
                ['status' => 400]
            );
        }

        return true;
    }

    /**
     * Validate target URL
     *
     * @param string          $value   Value to validate.
     * @param WP_REST_Request $request Current request object.
     * @param string          $param   Parameter name.
     * @return true|WP_Error
     */
    public function validate_target_url($value, $request, $param) {
        if (empty($value)) {
            return new WP_Error(
                'rest_invalid_param',
                __('Target URL cannot be empty.', 'prorank-seo'),
                ['status' => 400]
            );
        }

        // Allow relative URLs starting with /
        if (strpos($value, '/') === 0) {
            return true;
        }

        // Otherwise, must be a valid URL
        if (!filter_var($value, FILTER_VALIDATE_URL)) {
            return new WP_Error(
                'rest_invalid_param',
                __('Target must be a valid URL or start with /.', 'prorank-seo'),
                ['status' => 400]
            );
        }

        return true;
    }

    /**
     * Sanitize array of IDs
     *
     * @param array $ids Array of IDs.
     * @return array
     */
    public function sanitize_id_array($ids): array {
        if (!is_array($ids)) {
            return [];
        }

        return array_map('absint', $ids);
    }
}
