<?php

declare(strict_types=1);

namespace SiteFort\Modules\Cloudflare;

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

use SiteFort\Includes\Interfaces\Module;
use SiteFort\Includes\Module_Loader;

/**
 * Cloudflare integration controller.
 *
 * Routes AJAX/UI actions and plugin events into the Cloudflare Manager.
 */
class Cloudflare implements Module {

    private string $plugin_name;
    private string $version;
    private ?Manager $manager = null;

    public function __construct( string $plugin_name, string $version ) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
    }

    /**
     * Lazily build the Cloudflare manager. Deferred so anonymous front-end
     * requests, which only reach the reactive firewall-block hooks when an
     * attack is blocked, do not read the API credentials on every page view.
     */
    private function get_manager(): Manager {
        if ( null === $this->manager ) {
            $this->manager = new Manager();
        }
        return $this->manager;
    }

    public function initialize( Module_Loader $loader ) {
        // Internal cron reconciliation.
        $loader->add_action( Manager::CRON_HOOK, $this, 'run_reconcile_job', 10, 0 );

        // Firewall control-plane events. Runtime traffic is promoted
        // to Cloudflare only when the auto-block policy threshold is reached.
        $loader->add_action( 'sitefort_firewall_block', $this, 'on_firewall_block', 10, 1 );
        $loader->add_action( 'sitefort_firewall_lockout_changed', $this, 'on_firewall_lockout_changed', 10, 1 );
        $loader->add_action( 'sitefort_settings_saved_firewall', $this, 'on_firewall_settings_saved', 10, 1 );

        // AJAX endpoints used by admin app.
        $loader->add_action( 'wp_ajax_sitefort_cf_get_settings', $this, 'ajax_get_settings' );
        $loader->add_action( 'wp_ajax_sitefort_cf_verify', $this, 'ajax_verify' );
        $loader->add_action( 'wp_ajax_sitefort_cf_disconnect', $this, 'ajax_disconnect' );
        $loader->add_action( 'wp_ajax_sitefort_cf_save_settings', $this, 'ajax_save_settings' );
        $loader->add_action( 'wp_ajax_sitefort_cf_save_policy', $this, 'ajax_save_policy' );
        $loader->add_action( 'wp_ajax_sitefort_cf_sync', $this, 'ajax_sync' );
    }

    public function run_reconcile_job(): void {
        $result = $this->get_manager()->reconcile();
        do_action( 'sitefort_cloudflare_action', is_wp_error( $result ) ? 'sync_failed' : 'sync', '' );
    }

    /**
     * @param array<string,mixed> $event
     */
    public function on_firewall_block( array $event ): void {
        $this->get_manager()->on_firewall_block( $event );
    }

    /**
     * @param array<string,mixed> $payload
     */
    public function on_firewall_lockout_changed( array $payload ): void {
        $this->get_manager()->on_firewall_lockout_changed( $payload );
    }

    /**
     * @param array<string,mixed> $settings
     */
    public function on_firewall_settings_saved( array $settings ): void {
        if ( empty( $settings['enabled'] ) ) {
            $cloudflare_settings = \SiteFort\Includes\SettingsStore::get( \SiteFort\Includes\SettingsStore::COL_CLOUDFLARE, [] );
            if ( ! empty( $cloudflare_settings['enabled'] ) ) {
                $this->get_manager()->save_policy( [ 'enabled' => false ] );
            }
        }

        $this->get_manager()->schedule_reconcile( 20 );
    }

    public function ajax_get_settings(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        wp_send_json_success( $this->get_manager()->get_admin_settings() );
    }

    public function ajax_verify(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        $result = $this->get_manager()->verify_connection();
        if ( is_wp_error( $result ) ) {
            $data = $result->get_error_data();
            wp_send_json_error( [
                'message' => $result->get_error_message(),
                'code'    => $result->get_error_code(),
                'settings' => is_array( $data ) ? ( $data['settings'] ?? null ) : null,
                'permission_check' => is_array( $data ) ? ( $data['permission_check'] ?? null ) : null,
                'documentation_url' => is_array( $data ) ? ( $data['documentation_url'] ?? null ) : null,
                'resource' => is_array( $data ) ? ( $data['resource'] ?? null ) : null,
            ] );
            return;
        }

        wp_send_json_success( [
            'message'  => is_array( $result ) ? ( $result['message'] ?? 'Cloudflare credentials verified.' ) : 'Cloudflare credentials verified.',
            'settings' => is_array( $result ) ? ( $result['settings'] ?? $this->get_manager()->get_admin_settings() ) : $this->get_manager()->get_admin_settings(),
            'result'   => is_array( $result ) ? ( $result['result'] ?? $result ) : $result,
        ] );
    }

    public function ajax_disconnect(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        $result = $this->get_manager()->disconnect();
        wp_send_json_success( $result );
    }

    public function ajax_save_settings(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by verify_ajax_request() before reading request data.
        $zone_id = sanitize_text_field( wp_unslash( $_POST['zone_id'] ?? '' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by verify_ajax_request() before reading request data.
        $api_token = trim( sanitize_text_field( (string) wp_unslash( $_POST['api_token'] ?? '' ) ) );

        $result = $this->get_manager()->save_connection( $zone_id, $api_token );
        if ( is_wp_error( $result ) ) {
            $data = $result->get_error_data();
            wp_send_json_error( [
                'message' => $result->get_error_message(),
                'code'    => $result->get_error_code(),
                'settings' => is_array( $data ) ? ( $data['settings'] ?? null ) : null,
                'permission_check' => is_array( $data ) ? ( $data['permission_check'] ?? null ) : null,
                'documentation_url' => is_array( $data ) ? ( $data['documentation_url'] ?? null ) : null,
                'resource' => is_array( $data ) ? ( $data['resource'] ?? null ) : null,
            ] );
            return;
        }

        wp_send_json_success( $result );
    }

    public function ajax_save_policy(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        $policy = $this->parse_json_post_array( 'policy' );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by verify_ajax_request() before reading request data.
        if ( isset( $_POST['enabled'] ) ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce is verified by verify_ajax_request(), and rest_sanitize_boolean() normalizes the value.
            $policy['enabled'] = rest_sanitize_boolean( wp_unslash( $_POST['enabled'] ) );
        }

        $settings = $this->get_manager()->save_policy( $policy );

        wp_send_json_success( [
            'message'  => 'Cloudflare policy saved.',
            'settings' => $settings,
        ] );
    }

    public function ajax_sync(): void {
        if ( ! $this->verify_ajax_request() ) {
            return;
        }

        $result = $this->get_manager()->reconcile( true );
        if ( is_wp_error( $result ) ) {
            wp_send_json_error( [
                'message' => $result->get_error_message(),
                'code'    => $result->get_error_code(),
                'data'    => $result->get_error_data(),
            ] );
            return;
        }

        do_action( 'sitefort_cloudflare_action', 'sync', '' );
        wp_send_json_success( $result );
    }

    private function verify_ajax_request(): bool {
        check_ajax_referer( 'sitefort_nonce', '_ajax_nonce' );

        if ( ! \SiteFort\Includes\Capabilities::can_manage_plugin() ) {
            wp_send_json_error( [ 'message' => 'Unauthorized' ] );
            return false;
        }

        return true;
    }

    /**
     * @return array<string,mixed>
     */
    private function parse_json_post_array( string $key ): array {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce is verified before reading request data, and sanitize_post_value() normalizes the resulting value immediately.
        $raw = $this->sanitize_post_value( wp_unslash( $_POST[ $key ] ?? '' ) );
        if ( is_string( $raw ) && $raw !== '' ) {
            $decoded = json_decode( $raw, true );
            if ( is_array( $decoded ) ) {
                return $this->sanitize_post_value( $decoded );
            }
        }

        if ( is_array( $raw ) ) {
            return $raw;
        }

        return [];
    }

    /**
     * Sanitize scalar and nested request data before handing it to the manager layer.
     *
     * @param mixed $value Request value.
     * @return mixed
     */
    private function sanitize_post_value( $value ) {
        if ( is_array( $value ) ) {
            foreach ( $value as $array_key => $array_value ) {
                $value[ $array_key ] = $this->sanitize_post_value( $array_value );
            }

            return $value;
        }

        if ( is_string( $value ) ) {
            return sanitize_text_field( $value );
        }

        return $value;
    }
}
