<?php

/**
 * Register blocks and there scripts
 */
add_action('init', function () {
    /**
     * Form Block
     */
    // automatically load dependencies and version
    $block_asset_file = include dirname(__DIR__) . '/build/form-block.asset.php';
    $block = (array)json_decode(file_get_contents(__DIR__ . '/form/block.json'), true);

    wp_register_script(
        'ninja-forms/form',
        plugins_url('../build/form-block.js', __FILE__),
        $block_asset_file['dependencies'],
        $block_asset_file['version']
    );

    register_block_type('ninja-forms/form', array_merge($block, [
        'title' => esc_attr__('Ninja Form', 'ninja-forms'),
        'render_callback' => function ($atts) {
            $formID = isset($atts['formID']) ? $atts['formID'] : 1;
            ob_start();
            Ninja_Forms()->display( absint($formID), true );
            return ob_get_clean();
        },
        'editor_script' => 'ninja-forms/form'
    ]));


    /**
     * Views Block
     */
    // automatically load dependencies and version
    $block_asset_file = include dirname(__DIR__) . '/build/sub-table-block.asset.php';
    wp_register_script(
        'ninja-forms/submissions-table/block',
        plugins_url('../build/sub-table-block.js', __FILE__),
        $block_asset_file['dependencies'],
        $block_asset_file['version']
    );

    // Note: Token will be generated per-page in render_callback with specific form IDs

    $render_asset_file = include dirname(__DIR__) . '/build/sub-table-render.asset.php';
    wp_register_script(
        'ninja-forms/submissions-table/render',
        plugins_url('../build/sub-table-render.js', __FILE__),
        $render_asset_file['dependencies'],
        $render_asset_file['version']
    );

    register_block_type('ninja-forms/submissions-table', array(
        'editor_script' => 'ninja-forms/submissions-table/block',
        'render_callback' => function ($attributes, $content) {
            if (isset($attributes['formID']) && $attributes['formID']) {
                wp_enqueue_script('ninja-forms/submissions-table/render');

                // Generate a token bound to THIS specific form ID only
                $formId = absint($attributes['formID']);
                $token = NinjaForms\Blocks\Authentication\TokenFactory::make();
                $publicKey = NinjaForms\Blocks\Authentication\KeyFactory::make();

                // Create token with form ID binding and expiration
                wp_localize_script('ninja-forms/submissions-table/render', 'ninjaFormsViews', [
                    'token' => $token->create($publicKey, array($formId)),
                ]);
                
                // Enqueue signature fonts for proper display in Gutenberg block
                wp_enqueue_style(
                    'nf-signature-fonts',
                    Ninja_Forms::$url . 'assets/fonts/signature/google-fonts.css',
                    [],
                    Ninja_Forms::VERSION
                );

                $className = 'ninja-forms-views-submissions-table';
                if (isset($attributes['alignment'])) {
                    $className .= ' align' . $attributes['alignment'];
                }
                return sprintf("<div class='%s' data-attributes='%s'></div>", esc_attr($className),
                    esc_attr(wp_json_encode($attributes)));
            }
        }
    ));

    /**
     * Have Translations set in scripts via i18n package
     * https://developer.wordpress.org/block-editor/packages/packages-i18n/
     * https://developer.wordpress.org/reference/functions/wp_set_script_translations/
     * https://developer.wordpress.org/block-editor/developers/internationalization/
     */
    wp_set_script_translations( "ninja-forms/form", "ninja-forms", plugin_dir_path( __FILE__ ) . 'lang' );
    wp_set_script_translations( "ninja-forms/submissions-table/block", "ninja-forms", plugin_dir_path( __FILE__ ) . 'lang' );
    wp_set_script_translations( "ninja-forms/submissions-table/render", "ninja-forms", plugin_dir_path( __FILE__ ) . 'lang' );

});

/**
 * Localize data for blocks
 */
add_action('admin_enqueue_scripts', function () {
    //Conditionally load data for Blocks
    $screen = get_current_screen();
    if( is_null( $screen ) ) return;
    if( ! $screen->is_block_editor() ) return;
        //Get all forms, to base form selector on.
        $formsBuilder = (new NinjaForms\Blocks\DataBuilder\FormsBuilderFactory)->make();
        $forms = $formsBuilder->get();
        if (!empty($forms)) {
            //Escape for use in JavaScript
            foreach ($forms as $key => $form) {
                $forms[$key] = [
                    'formID' => absint($form['formID']),
                    'formTitle' => esc_textarea($form['formTitle'])
                ];
            }
        }
    wp_localize_script('ninja-forms/form', 'nfFormsBlock', [
        'forms' => $forms,//array keys escaped above
        'homeUrl' => esc_url_raw( home_url() ), //URL to serve the iFrame that displays the form in blocks editor
        'previewToken' => wp_create_nonce('nf_iframe' )
    ]);

    // For block editor, provide a token that allows access to all forms
    // This is safe because it's only loaded in admin context with proper capability checks
    $token = NinjaForms\Blocks\Authentication\TokenFactory::make();
    $publicKey = NinjaForms\Blocks\Authentication\KeyFactory::make();
    $allFormIds = array_map(function($form) { return absint($form['formID']); }, $forms);

    wp_localize_script('ninja-forms/submissions-table/block', 'ninjaFormsViews', [
        'token' => $token->create($publicKey, $allFormIds),
    ]);
});

/**
 * Register REST API routes related to blocks
 */
add_action('rest_api_init', function () {

    /**
     * Enhanced permission callback that validates token and checks form-level authorization.
     *
     * Security improvements:
     * - Rate limiting to prevent DoS attacks
     * - Validates token authenticity (hash, expiration)
     * - Checks if token is authorized for the requested form ID
     * - Falls back to WordPress capability check for admin users
     *
     * @param WP_REST_Request $request
     * @return bool|WP_Error
     */
    $tokenAuthenticationCallback = function (WP_REST_Request $request) {
        // Check rate limit first (lightweight check)
        $endpoint = $request->get_route();
        $rateLimitCheck = NinjaForms\Blocks\Authentication\RateLimiter::check($endpoint);
        if (is_wp_error($rateLimitCheck)) {
            return $rateLimitCheck;
        }

        $tokenValidator = NinjaForms\Blocks\Authentication\TokenFactory::make();
        $tokenHeader = $request->get_header('X-NinjaFormsViews-Auth');
        $formId = $request->get_param('id');

        // If user is logged in and has manage_options capability, allow access
        // This provides fallback for admin users
        if (is_user_logged_in() && current_user_can('manage_options')) {
            return true;
        }

        // Validate token with form ID authorization
        if ($formId) {
            return $tokenValidator->validate($tokenHeader, intval($formId));
        }

        // For routes without a specific form ID (like /forms list), only validate token structure
        // The token must still be valid (not expired, proper signature)
        return $tokenValidator->validate($tokenHeader);
    };

    register_rest_route('ninja-forms-views', 'forms', array(
        'methods' => 'GET',
        'callback' => function (WP_REST_Request $request) {
            $tokenValidator = NinjaForms\Blocks\Authentication\TokenFactory::make();
            $tokenHeader = $request->get_header('X-NinjaFormsViews-Auth');

            // Get all forms
            $formsBuilder = (new NinjaForms\Blocks\DataBuilder\FormsBuilderFactory)->make();
            $allForms = $formsBuilder->get();

            // If user has manage_options capability, return all forms
            if (is_user_logged_in() && current_user_can('manage_options')) {
                return $allForms;
            }

            // Otherwise, filter forms based on token authorization
            $authorizedFormIds = $tokenValidator->getFormIds($tokenHeader);
            if ($authorizedFormIds === false) {
                return new WP_Error('invalid_token', 'Invalid token', array('status' => 403));
            }

            // Filter to only return forms the token has access to
            $filteredForms = array_filter($allForms, function($form) use ($authorizedFormIds) {
                return in_array(intval($form['formID']), $authorizedFormIds, true);
            });

            return array_values($filteredForms);
        },
        'permission_callback' => $tokenAuthenticationCallback,
    ));

    register_rest_route('ninja-forms-views', 'forms/(?P<id>\d+)/fields', [
        'methods' => 'GET',
        'args' => [
            'id' => [
                'required' => true,
                'description' => esc_attr__('Unique identifier for the object.', 'ninja-forms'),
                'type' => 'integer',
                'validate_callback' => 'rest_validate_request_arg',
            ],
        ],
        'callback' => function (WP_REST_Request $request) {
            $fieldsBuilder = (new NinjaForms\Blocks\DataBuilder\FieldsBuilderFactory)->make(
                $request->get_param('id')
            );
            return $fieldsBuilder->get();
        },
        'permission_callback' => $tokenAuthenticationCallback,
    ]);

    register_rest_route('ninja-forms-views', 'forms/(?P<id>\d+)/submissions', [
        'methods' => 'GET',
        'args' => [
            'id' => [
                'required' => true,
                'description' => esc_attr__('Unique identifier for the object.', 'ninja-forms'),
                'type' => 'integer',
                'validate_callback' => 'rest_validate_request_arg',
            ],
            'perPage' => [
                'description' => esc_attr__('Maximum number of items to be returned in result set.', 'ninja-forms'),
                'type' => 'integer',
                'minimum' => 1,
                'maximum' => 100,
                'sanitize_callback' => 'absint',
                'validate_callback' => 'rest_validate_request_arg',
            ],
            'page' => [
                'description' => esc_attr__('Current page of the collection.', 'ninja-forms'),
                'type' => 'integer',
                'default' => 1,
                'sanitize_callback' => 'absint',
                'validate_callback' => 'rest_validate_request_arg',
                'minimum' => 1,
            ]
        ],
        'callback' => function (WP_REST_Request $request) {
            $submissionsBuilder = (new NinjaForms\Blocks\DataBuilder\SubmissionsBuilderFactory)->make(
                $request->get_param('id'),
                $request->get_param('perPage'),
                $request->get_param('page')
            );
            return $submissionsBuilder->get();
        },
        'permission_callback' => $tokenAuthenticationCallback,
    ]);

    /**
     * Token Refresh Endpoint
     *
     * Generates a new token scoped to requested form IDs.
     * Used for automatic token refresh when tokens expire or after secret rotation.
     *
     * FIX: Restricts token generation to single forms and validates form access
     */
    register_rest_route('ninja-forms-views', 'token/refresh', array(
        'methods' => 'POST',
        'callback' => function (WP_REST_Request $request) {
            // REFACTOR: Accept single formID instead of formIds array
            $formId = $request->get_param('formID');
            
            // Check for legacy formIds parameter for backward compatibility
            if (!$formId && $request->get_param('formIds')) {
                $formIds = $request->get_param('formIds');
                if (is_array($formIds) && !empty($formIds)) {
                    // Only accept single form from legacy array
                    if (count($formIds) > 1) {
                        return new WP_Error(
                            'too_many_form_ids',
                            __('Token generation is limited to one form at a time. Please use formID parameter instead.', 'ninja-forms'),
                            array('status' => 400)
                        );
                    }
                    $formId = $formIds[0];
                }
            }

            // Sanitize and validate form ID
            $formId = absint($formId);
            
            if (!$formId) {
                return new WP_Error(
                    'invalid_form_id',
                    __('Valid form ID is required', 'ninja-forms'),
                    array('status' => 400)
                );
            }

            // FIX: Validate that the form exists and is accessible
            $form = Ninja_Forms()->form( $formId )->get();
            if (!$form) {
                return new WP_Error(
                    'form_not_found',
                    __('The requested form does not exist', 'ninja-forms'),
                    array('status' => 404)
                );
            }

            // FIX: Validate that user has permission to access this form
            // This prevents users from generating tokens for arbitrary forms
            $referer = wp_get_referer();
            if (!$referer) {
                return new WP_Error(
                    'invalid_request',
                    __('Request must come from a valid page with submissions table block', 'ninja-forms'),
                    array('status' => 403)
                );
            }

            // Parse the referring page to validate block authorization
            $post_id = url_to_postid($referer);
            if (!$post_id) {
                // Handle front page, archives, etc.
                $parsed_url = parse_url($referer);
                if ($parsed_url['path'] === '/' || $parsed_url['path'] === home_url('/')) {
                    $post_id = get_option('page_on_front');
                }
            }

            // Check if the form is actually embedded in a submissions table block on this page
            if ($post_id) {
                $post = get_post($post_id);
                $is_public = is_post_publicly_viewable( $post );

                // If post is public _and_ password-protected, but user hasn't provided a valid password
                if( $is_public && post_password_required( $post ) ) {
                    return new WP_Error(
                        'unauthorized_form_access',
                        __('You do not have permission to access this form via this page', 'ninja-forms'),
                        array('status' => 403)
                    );
                }

                // If post is private or just generally not public, and logged-in user cannot read it
                if( ! $is_public && ! current_user_can( 'read_post', $post ) ) {
                    return new WP_Error(
                        'unauthorized_form_access',
                        __('You do not have permission to access this form via this page', 'ninja-forms'),
                        array('status' => 403)
                    );
                }

                if ($post && has_blocks($post->post_content)) {
                    $blocks = parse_blocks($post->post_content);
                    $found_authorized_form = false;
                    
                    // Recursively search for ninja-forms/submissions-table blocks
                    $search_blocks = function($blocks) use ($formId, &$found_authorized_form, &$search_blocks) {
                        foreach ($blocks as $block) {
                            if ($block['blockName'] === 'ninja-forms/submissions-table') {
                                if (isset($block['attrs']['formID']) && 
                                    intval($block['attrs']['formID']) === $formId) {
                                    $found_authorized_form = true;
                                    return;
                                }
                            }
                            // Search inner blocks recursively
                            if (!empty($block['innerBlocks'])) {
                                $search_blocks($block['innerBlocks']);
                            }
                        }
                    };
                    
                    $search_blocks($blocks);
                    
                    if (!$found_authorized_form) {
                        return new WP_Error(
                            'unauthorized_form_access',
                            __('You do not have permission to access this form via this page', 'ninja-forms'),
                            array('status' => 403)
                        );
                    }
                }
            } else {
                // If we can't determine the post ID, return an error
                return new WP_Error(
                    'post_id_not_found',
                    __('The requested data could not be related to a valid page', 'ninja-forms'),
                    array('status' => 403)
                );
            }

            // Generate new token scoped to the single requested form
            $publicKey = NinjaForms\Blocks\Authentication\KeyFactory::make(32);
            $tokenGenerator = NinjaForms\Blocks\Authentication\TokenFactory::make();
            $newToken = $tokenGenerator->create($publicKey, array($formId));

            return array(
                'token' => $newToken,
                'publicKey' => $publicKey,
                'expiresIn' => 900, // 15 minutes in seconds
                'formID' => $formId, // Changed from formIds to formID
            );
        },
        'permission_callback' => function (WP_REST_Request $request) {
            // Apply stricter rate limiting to refresh endpoint
            $rateLimitCheck = NinjaForms\Blocks\Authentication\RateLimiter::check(
                '/ninja-forms-views/token/refresh',
                50,  // limit: 50 requests
                300  // window: 5 minutes
            );

            if (is_wp_error($rateLimitCheck)) {
                return $rateLimitCheck; // Returns 429 Too Many Requests
            }

            return true; // Public endpoint (rate-limited) but with form validation
        },
    ));

});

/**
 * Handler for form preview iFrame used in Forms block
 */
add_action( 'wp_head', function () {
    // check for preview and iframe get parameters
    if( isset( $_GET[ 'nf_preview_form' ] ) && isset( $_GET[ 'nf_iframe' ] ) ){
        if( ! wp_verify_nonce( $_GET['nf_iframe'], 'nf_iframe') ){
            wp_die( esc_html__('Preview token failed validation', 'ninja-forms'));
            exit;
        }

        //Attempt to get theme background color
        $background = '#fff';
        $supports = get_theme_support('editor-color-palette','background');
        if( is_array($supports) ){
            foreach($supports[0] as $index => $support ){
                if( 'background' === $support['slug']){
                    $background = $support['color'];
                    break;
                }
            }
        }

        $js_lib_dir  = Ninja_Forms::$url . 'assets/js/lib/';

        $form_id = absint( $_GET[ 'nf_preview_form' ] );
        // Style below: update width and height for particular form
        ?>
        <style media="screen">
            #wpadminbar {
                display: none;
            }
            #nf-form-<?php echo $form_id; ?>-cont {
                z-index: 90000001;
                position: fixed;
                top: 0; left: 0;
                width: 100vw;
                height: 100vh;
                background-color: <?php echo sanitize_hex_color($background ); ?>;
            }

            div.site-branding, header.entry-header, .site-footer, header, .footer-nav-widgets-wrapper {
                display:none !important;
            }

        </style>

        <?php

        // register our script to target the form iFrame in page builder
        wp_register_script(
            'ninja-forms-block-setup',
            $js_lib_dir . 'blockFrameSetup.js',
            array( 'underscore', 'jquery' )
        );

        wp_localize_script( 'ninja-forms-block-setup', 'ninjaFormsBlockSetup', array(
            'form_id' => $form_id
        ) );

        wp_enqueue_script( 'ninja-forms-block-setup' );
    }

});

/**
 * Schedule WP-Cron job for automatic secret rotation
 */
add_action('init', function() {
    if (!wp_next_scheduled('ninja_forms_views_check_rotation')) {
        wp_schedule_event(time(), 'daily', 'ninja_forms_views_check_rotation');
    }
});

/**
 * WP-Cron callback: Check if secret should be rotated and rotate if needed
 */
add_action('ninja_forms_views_check_rotation', function() {
    if (NinjaForms\Blocks\Authentication\SecretStore::shouldRotate()) {
        NinjaForms\Blocks\Authentication\SecretStore::rotate();
    }
});

/**
 * Clear scheduled events on plugin deactivation
 */
register_deactivation_hook(__FILE__, function() {
    $timestamp = wp_next_scheduled('ninja_forms_views_check_rotation');
    if ($timestamp) {
        wp_unschedule_event($timestamp, 'ninja_forms_views_check_rotation');
    }
});
