<?php

namespace UltraguardPro\modules\api_security;

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

use UltraguardPro\Plugin;
use UltraguardPro\core\Utils;
use UltraguardPro\modules\core\AbstractModule;

class ApiSecurityModule extends AbstractModule {

    public function __construct( Plugin $plugin ) {
        parent::__construct( $plugin );
        $this->plugin->settings->register_module_prefix( 'api_security_', 'api_security' );
    }

    public function get_id(): string {
        return 'api_security';
    }

    protected function requires_pro(): bool {
        return true;
    }

    protected function allow_preview_mode(): bool {
        return true;
    }

    public function boot(): void {
        $_db_tables = $this->plugin->modules->get_db_tables();
        if ( $_db_tables ) {
            $_db_tables->register_schema( new schema\ApiSecuritySchema() );
        }

        if ( method_exists( $this->plugin->settings, 'register_settings_schema' ) ) {
            $this->plugin->settings->register_settings_schema( 'api_security', 'api_security_', [
                'api_security_enabled'               => [ 'type' => 'bool', 'default' => true ],
                'api_security_rest_mode'             => [ 'type' => 'select', 'default' => 'public', 'options' => [ 'public', 'auth_required', 'per_route' ] ],
                'api_security_blocked_routes'        => [ 'type' => 'textarea', 'default' => '' ],
                'api_security_allowed_routes'        => [ 'type' => 'textarea', 'default' => '' ],
                'api_security_enable_api_keys'       => [ 'type' => 'bool', 'default' => true ],
                'api_security_enable_xmlrpc'         => [ 'type' => 'bool', 'default' => true ],
                'api_security_xmlrpc_mode'           => [ 'type' => 'select', 'default' => 'allow_all', 'options' => [ 'allow_all', 'disable', 'allow_specific' ] ],
                'api_security_xmlrpc_methods'        => [ 'type' => 'textarea', 'default' => '' ],
                'api_security_log_rest'              => [ 'type' => 'bool', 'default' => true ],
                'api_security_log_xmlrpc'            => [ 'type' => 'bool', 'default' => true ],
                'api_security_log_retention_days'    => [ 'type' => 'number', 'default' => 7 ],
                'api_security_default_rate_limit'    => [ 'type' => 'number', 'default' => 60 ],
            ] );
        }

        ( new ajax\ApiSecurityAjax( $this->plugin ) )->register();
        add_action( 'ultraguard_register_admin_pages', [ $this, 'register_pages' ], 18, 1 );
        ( new ApiSecurityDashboardContributor( $this->plugin ) )->register();
        add_action( 'ultraguard_after_enqueue_assets', [ $this, 'enqueue_assets' ] );

        if ( ! $this->is_preview_mode() ) {
            add_filter( 'rest_authentication_errors', [ $this, 'filter_rest_authentication_errors' ], 10, 1 );
            add_filter( 'rest_pre_dispatch', [ $this, 'filter_rest_pre_dispatch' ], 5, 3 );
            add_filter( 'xmlrpc_enabled', [ $this, 'filter_xmlrpc_enabled' ] );
            add_filter( 'xmlrpc_methods', [ $this, 'filter_xmlrpc_methods' ] );
            add_action( 'xmlrpc_call', [ $this, 'log_xmlrpc_call' ], 10, 1 );
            add_filter( 'rest_request_after_callbacks', [ $this, 'log_rest_request' ], 10, 3 );
        }

        do_action( 'ultraguard_pro/api_security/booted', $this );
    }

    public function register_pages( $admin ): void {
        $admin->register_page( [
            'id'              => 'api-security',
            'page_title'      => __( 'REST API & XML-RPC Security', 'ultraguard-security-suite' ),
            'menu_title'      => __( 'API Security', 'ultraguard-security-suite' ),
            'capability'      => 'manage_options',
            'scheme_callback' => [ new pages\ApiSecurityDashboard( $this->plugin ), 'get_scheme' ],
            'parent_id'       => '',
            'priority'        => 18,
            'group'           => 'Protection',
            'icon'            => 'dashicons-rest-api',
        ] );

        $admin->register_page( [
            'id'              => 'api-security-settings',
            'page_title'      => __( 'API Security Settings', 'ultraguard-security-suite' ),
            'menu_title'      => __( 'Settings', 'ultraguard-security-suite' ),
            'capability'      => 'manage_options',
            'scheme_callback' => [ new pages\ApiSecuritySettings( $this->plugin ), 'get_scheme' ],
            'parent_id'       => 'api-security',
            'priority'        => 19,
            'group'           => 'Protection',
            'icon'            => 'dashicons-admin-settings',
        ] );
    }

    public function filter_rest_authentication_errors( $result ) {
        if ( is_wp_error( $result ) || ! $this->option_enabled( 'api_security_enabled' ) ) {
            return $result;
        }

        if ( ! $this->option_enabled( 'api_security_enable_api_keys' ) ) {
            return $result;
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_URI is unslashed and parsed immediately below for internal REST inspection.
        if ( isset( $_SERVER['REQUEST_URI'] ) && false !== strpos( (string) wp_unslash( $_SERVER['REQUEST_URI'] ), '/wp-json/' ) ) {
            $engine = new ApiSecurityEngine( $this->plugin );
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_URI is unslashed and reduced to the path component immediately.
            $uri          = (string) wp_parse_url( (string) wp_unslash( $_SERVER['REQUEST_URI'] ), PHP_URL_PATH );
            $rest_path    = preg_replace( '#^.*?/wp-json#', '', $uri );
            $rest_path    = $rest_path ? $rest_path : '/';
            $rest_request = new \WP_REST_Request(
                sanitize_text_field( (string) wp_unslash( $_SERVER['REQUEST_METHOD'] ?? 'GET' ) ),
                sanitize_text_field( $rest_path )
            );
            $auth_header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? sanitize_text_field( (string) wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ) ) : '';
            if ( '' !== $auth_header ) {
                $rest_request->set_header( 'authorization', $auth_header );
            }
            $validation = $engine->validate_api_key( $rest_request );
            if ( is_wp_error( $validation ) ) {
                return $validation;
            }
        }

        return $result;
    }

    public function filter_rest_pre_dispatch( $response, $server, $request ) {
        if ( ! $this->option_enabled( 'api_security_enabled' ) ) {
            return $response;
        }

        return ( new ApiSecurityEngine( $this->plugin ) )->enforce_route_rules( $request, $server, $response );
    }

    public function filter_xmlrpc_enabled( bool $enabled ): bool {
        if ( ! $this->option_enabled( 'api_security_enabled' ) || ! $this->option_enabled( 'api_security_enable_xmlrpc' ) ) {
            return $enabled;
        }

        return 'disable' !== (string) $this->option( 'api_security_xmlrpc_mode', 'allow_all' );
    }

    public function filter_xmlrpc_methods( array $methods ): array {
        if ( ! $this->option_enabled( 'api_security_enabled' ) || ! $this->option_enabled( 'api_security_enable_xmlrpc' ) ) {
            return $methods;
        }

        if ( 'allow_specific' !== (string) $this->option( 'api_security_xmlrpc_mode', 'allow_all' ) ) {
            return $methods;
        }

        $engine  = new ApiSecurityEngine( $this->plugin );
        $allowed = [];

        foreach ( array_keys( $methods ) as $method ) {
            if ( $engine->check_xmlrpc_method( (string) $method ) ) {
                $allowed[ $method ] = $methods[ $method ];
            }
        }

        return $allowed;
    }

    public function log_xmlrpc_call( string $method ): void {
        if ( ! $this->option_enabled( 'api_security_log_xmlrpc' ) ) {
            return;
        }

        ( new ApiSecurityEngine( $this->plugin ) )->log_request( [
            'endpoint'      => $method,
            'method'        => 'POST',
            'ip_address'    => Utils::get_client_ip(),
            'auth_type'     => 'none',
            'key_id'        => 0,
            'response_code' => 200,
            'was_blocked'   => 0,
            'block_reason'  => '',
            'request_type'  => 'xmlrpc',
        ] );
    }

    public function log_rest_request( $response, $handler, $request ) {
        if ( ! $this->option_enabled( 'api_security_log_rest' ) ) {
            return $response;
        }

        $code = 200;
        if ( is_wp_error( $response ) ) {
            $code = 403;
        } elseif ( is_object( $response ) && method_exists( $response, 'get_status' ) ) {
            $code = (int) $response->get_status();
        } elseif ( is_array( $response ) && isset( $response['status'] ) ) {
            $code = (int) $response['status'];
        }
        ( new ApiSecurityEngine( $this->plugin ) )->log_request( [
            'endpoint'      => (string) $request->get_route(),
            'method'        => (string) $request->get_method(),
            'ip_address'    => Utils::get_client_ip(),
            'auth_type'     => is_user_logged_in() ? 'cookie' : 'none',
            'key_id'        => 0,
            'response_code' => $code,
            'was_blocked'   => is_wp_error( $response ) ? 1 : 0,
            'block_reason'  => is_wp_error( $response ) ? $response->get_error_code() : '',
            'request_type'  => 'rest',
        ] );

        return $response;
    }

    public function enqueue_assets(): void {
        $ug_page = sanitize_key( wp_unslash( $_GET['ug_page'] ?? ''  ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only screen detection.
        if ( ! in_array( $ug_page, [ 'api-security', 'api-security-settings' ], true ) ) {
            return;
        }

        $module_url = plugin_dir_url( __FILE__ );
        $version    = defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '1.0.0';

        wp_enqueue_style(
            'ultraguard-api-security-page',
            $module_url . 'assets/css/api-security-page.css',
            [ 'admin-base', 'admin-components' ],
            $version
        );

        wp_enqueue_script(
            'ultraguard-api-security-page',
            $module_url . 'assets/js/api-security-page.js',
            [ 'jquery', 'ultraguard-core', 'ultraguard-managers' ],
            $version,
            true
        );

        wp_localize_script( 'ultraguard-api-security-page', 'ultraguardApiSecurity', [
            'nonce'   => wp_create_nonce( 'ultraguard_admin_nonce' ),
            'ajaxUrl' => self_admin_url( 'admin-ajax.php' ),
            'i18n'    => [
                'creating' => __( 'Creating API key...', 'ultraguard-security-suite' ),
                'created'  => __( 'API key created. Copy it now because it will only be shown once.', 'ultraguard-security-suite' ),
                'failed'   => __( 'Unable to complete the requested API security action.', 'ultraguard-security-suite' ),
            ],
        ] );
    }
}
