<?php

namespace UltraguardPro\modules\api_security;

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

use UltraguardPro\Plugin;
use UltraguardPro\core\BaseController;
use UltraguardPro\core\DatabaseCrud;

class ApiSecurityEngine extends BaseController {

    public function __construct( Plugin $plugin ) {
        parent::__construct( $plugin );
    }

    public function validate_api_key( \WP_REST_Request $request ) {
        $header = (string) $request->get_header( 'authorization' );
        if ( '' === $header || ! preg_match( '/Bearer\s+(.+)/i', $header, $matches ) ) {
            return true;
        }

        $token = sanitize_text_field( trim( (string) $matches[1] ) );
        $table = $this->getTable( 'api_keys' );
        if ( ! $table ) {
            return new \WP_Error( 'api_security_unavailable', __( 'API key storage is unavailable.', 'ultraguard-security-suite' ), [ 'status' => 500 ] );
        }

        $keys = $table->get_by( [ 'is_active' => 1 ], 200, 'id', 'DESC' );
        foreach ( $keys as $row ) {
            if ( ! hash_equals( (string) $row['api_key'], wp_hash( $token ) ) ) {
                continue;
            }

            if ( ! empty( $row['expires_at'] ) && strtotime( (string) $row['expires_at'] ) < time() ) {
                return new \WP_Error( 'api_key_expired', __( 'API key has expired.', 'ultraguard-security-suite' ), [ 'status' => 403 ] );
            }

            if ( ! $this->is_route_allowed( $request, $row ) ) {
                return new \WP_Error( 'api_key_route_denied', __( 'API key is not allowed for this route.', 'ultraguard-security-suite' ), [ 'status' => 403 ] );
            }

            if ( ! $this->apply_rate_limit( $this->resolve_ip(), (int) $row['id'], (string) $request->get_route(), (int) $row['rate_limit'] ) ) {
                return new \WP_Error( 'api_rate_limited', __( 'API rate limit exceeded.', 'ultraguard-security-suite' ), [ 'status' => 429 ] );
            }

            if ( method_exists( $table, 'update' ) ) {
                $table->update( (int) $row['id'], [ 'last_used_at' => current_time( 'mysql', true ) ] );
            }

            return true;
        }

        return new \WP_Error( 'invalid_api_key', __( 'Invalid API key.', 'ultraguard-security-suite' ), [ 'status' => 403 ] );
    }

    public function enforce_route_rules( \WP_REST_Request $request, \WP_REST_Server $server, $response = null ) {
        $route   = (string) $request->get_route();
        $mode    = (string) $this->plugin->settings->get_option( 'api_security_rest_mode', 'public' );
        $allowed = $this->lines_to_patterns( (string) $this->plugin->settings->get_option( 'api_security_allowed_routes', '' ) );
        $blocked = $this->lines_to_patterns( (string) $this->plugin->settings->get_option( 'api_security_blocked_routes', '' ) );

        foreach ( $allowed as $pattern ) {
            if ( @preg_match( '#' . $pattern . '#', $route ) ) {
                return $response;
            }
        }

        foreach ( $blocked as $pattern ) {
            if ( @preg_match( '#' . $pattern . '#', $route ) ) {
                return new \WP_Error( 'api_route_blocked', __( 'This API route is blocked.', 'ultraguard-security-suite' ), [ 'status' => 403 ] );
            }
        }

        if ( 'auth_required' === $mode && ! is_user_logged_in() ) {
            $auth = $this->validate_api_key( $request );
            if ( is_wp_error( $auth ) ) {
                return $auth;
            }
        }

        if ( 'per_route' === $mode && ! $this->apply_rate_limit( $this->resolve_ip(), 0, $route, (int) $this->plugin->settings->get_option( 'api_security_default_rate_limit', 60 ) ) ) {
            return new \WP_Error( 'api_rate_limited', __( 'Too many API requests.', 'ultraguard-security-suite' ), [ 'status' => 429 ] );
        }

        return $response;
    }

    public function apply_rate_limit( string $ip, int $key_id, string $endpoint, int $limit = 0 ): bool {
        $limit = $limit > 0 ? $limit : (int) $this->plugin->settings->get_option( 'api_security_default_rate_limit', 60 );
        if ( $limit <= 0 ) {
            return true;
        }

        $bucket = gmdate( 'YmdHi' );
        $key    = 'ultraguard_api_rl_' . md5( $ip . '|' . $key_id . '|' . $endpoint . '|' . $bucket );
        $count  = (int) get_transient( $key );
        if ( $count >= $limit ) {
            return false;
        }

        set_transient( $key, $count + 1, MINUTE_IN_SECONDS );
        return true;
    }

    public function log_request( array $data ): void {
        $table = $this->getTable( 'api_request_log' );
        if ( ! $table || ! method_exists( $table, 'insert' ) ) {
            return;
        }

        $table->insert( $data );
        $this->prune_old_logs();
    }

    public function check_xmlrpc_method( string $method ): bool {
        $mode = (string) $this->plugin->settings->get_option( 'api_security_xmlrpc_mode', 'allow_all' );
        if ( 'disable' === $mode ) {
            return false;
        }

        if ( 'allow_specific' !== $mode ) {
            return true;
        }

        $methods = array_map( 'trim', preg_split( '/\r\n|\r|\n/', (string) $this->plugin->settings->get_option( 'api_security_xmlrpc_methods', '' ) ) );
        return in_array( $method, array_filter( $methods ), true );
    }

    public function generate_api_key(): array {
        $raw = wp_generate_password( 40, false, false ) . wp_generate_password( 24, true, true );
        return [
            'key'    => $raw,
            'prefix' => substr( $raw, 0, 8 ),
            'hash'   => wp_hash( $raw ),
        ];
    }

    public function get_stats(): array {
        $since = gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS );

        return [
            'total_api_keys'    => (int) DatabaseCrud::get_count( 'ultraguard_api_keys' ),
            'rest_requests_24h' => DatabaseCrud::count_since( 'ultraguard_api_request_log', [ 'request_type' => 'rest' ], 'created_at', $since ),
            'blocked_24h'       => DatabaseCrud::count_since( 'ultraguard_api_request_log', [ 'was_blocked' => 1 ], 'created_at', $since ),
            'xmlrpc_calls_24h'  => DatabaseCrud::count_since( 'ultraguard_api_request_log', [ 'request_type' => 'xmlrpc' ], 'created_at', $since ),
        ];
    }

    private function is_route_allowed( \WP_REST_Request $request, array $row ): bool {
        $route   = (string) $request->get_route();
        $method  = strtoupper( (string) $request->get_method() );
        $routes  = ! empty( $row['allowed_routes'] ) ? json_decode( (string) $row['allowed_routes'], true ) : [];
        $methods = ! empty( $row['allowed_methods'] ) ? json_decode( (string) $row['allowed_methods'], true ) : [];

        if ( is_array( $methods ) && ! empty( $methods ) && ! in_array( $method, $methods, true ) ) {
            return false;
        }

        if ( ! is_array( $routes ) || empty( $routes ) ) {
            return true;
        }

        foreach ( $routes as $pattern ) {
            if ( @preg_match( '#' . $pattern . '#', $route ) ) {
                return true;
            }
        }

        return false;
    }

    private function lines_to_patterns( string $value ): array {
        return array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', $value ) ) ) );
    }

    private function prune_old_logs(): void {
        $days = max( 1, min( 90, (int) $this->plugin->settings->get_option( 'api_security_log_retention_days', 7 ) ) );
        $table = $this->getTable( 'api_request_log' );
        if ( $table && method_exists( $table, 'prune_records' ) ) {
            $table->prune_records( $days );
            return;
        }

        DatabaseCrud::delete_old_records( 'ultraguard_api_request_log', 'created_at', $days, true );
    }

    private function resolve_ip(): string {
        return $this->getClientIp();
    }
}
