<?php

namespace Storeplugin\PDFInvoice\Framework\ListTables;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\Framework\Contracts\DataSourceInterface;
use Storeplugin\PDFInvoice\Framework\Utils\RateLimiter;
use Storeplugin\PDFInvoice\Framework\Utils\SecurityLogger;
use Storeplugin\PDFInvoice\Config;

/**
 * Registry for draggable list tables
 * Maps table IDs to their data sources for Ajax handling
 */
class DraggableRegistry {
    /**
     * Registered data sources
     *
     * @var array
     */
    private static $dataSources = [];

    /**
     * Registered configurations
     *
     * @var array
     */
    private static $configs = [];

    /**
     * Whether hooks are registered
     *
     * @var bool
     */
    private static $hooksRegistered = false;

    /**
     * Register a draggable table
     *
     * @param string              $tableId    Unique table identifier
     * @param DataSourceInterface $dataSource Data source instance
     * @param array               $config      Configuration options
     */
    public static function register( $tableId, DataSourceInterface $dataSource, array $config = [] ) {
        self::$dataSources[ $tableId ] = $dataSource;
        self::$configs[ $tableId ] = wp_parse_args( $config, [
            'capability' => 'manage_options',
            'handle_column' => null,
        ] );

        // Register Ajax hooks on first registration
        if ( ! self::$hooksRegistered ) {
            self::registerAjaxHooks();
            self::$hooksRegistered = true;
        }
    }

    /**
     * Get a registered data source
     *
     * @param string $tableId Table identifier
     *
     * @return DataSourceInterface|null
     */
    public static function getDataSource( $tableId ) {
        return self::$dataSources[ $tableId ] ?? null;
    }

    /**
     * Get configuration for a table
     *
     * @param string $tableId Table identifier
     *
     * @return array|null
     */
    public static function getConfig( $tableId ) {
        return self::$configs[ $tableId ] ?? null;
    }

    /**
     * Check if a table is registered
     *
     * @param string $tableId Table identifier
     *
     * @return bool
     */
    public static function isTableRegistered( $tableId ) {
        return isset( self::$dataSources[ $tableId ] );
    }

    /**
     * Register Ajax hooks
     */
    private static function registerAjaxHooks() {
        add_action( 'wp_ajax_' . Config::ajax('update_table_order'), [ __CLASS__, 'handleAjaxUpdateOrder' ] );
    }

    /**
     * Handle Ajax request to update table order
     */
    public static function handleAjaxUpdateOrder() {
        // Rate limiting: 30 requests per minute per user/IP
        if ( ! RateLimiter::checkAjax( 'update_table_order', 30, 60 ) ) {
            SecurityLogger::logRateLimitExceeded( 'update_table_order', [
                'table_id' => isset( $_POST['table_id'] ) ? \sanitize_text_field( wp_unslash( $_POST['table_id'] ) ) : 'unknown',
            ] );
            return; // checkAjax sends the error response
        }

        // Verify nonce
        $nonce = isset( $_POST['nonce'] ) ? \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ) : '';
        if ( ! $nonce || ! wp_verify_nonce( $nonce, Config::nonce('draggable') ) ) {
            SecurityLogger::logNonceFailed( Config::nonce('draggable'), [
                'ajax_action' => Config::ajax('update_table_order'),
            ] );
            wp_send_json_error( [
                'message' => __( 'Security check failed.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
            ], 403 );
        }

        // Get table ID
        $tableId = isset( $_POST['table_id'] ) ? sanitize_text_field( wp_unslash( $_POST['table_id'] ) ) : '';
        if ( empty( $tableId ) ) {
            wp_send_json_error( [
                'message' => __( 'Table ID is required.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
            ], 400 );
        }

        // Get data source
        $dataSource = self::getDataSource( $tableId );
        if ( ! $dataSource ) {
            wp_send_json_error( [
                'message' => __( 'Table not found.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
            ], 404 );
        }

        // Check capability
        $config = self::getConfig( $tableId );
        $capability = $config['capability'] ?? 'manage_options';
        if ( ! current_user_can( $capability ) ) {
            SecurityLogger::logPermissionDenied( $capability, [
                'ajax_action' => Config::ajax('update_table_order'),
                'table_id' => $tableId,
            ] );
            wp_send_json_error( [
                'message' => __( 'You do not have permission to perform this action.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
            ], 403 );
        }

        // Get ordered IDs
        $orderedIds = isset( $_POST['ordered_ids'] ) ? array_map( 'intval', $_POST['ordered_ids'] ) : [];
        if ( empty( $orderedIds ) ) {
            wp_send_json_error( [
                'message' => __( 'No items to reorder.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
            ], 400 );
        }

        // Update order
        try {
            $result = $dataSource->updateOrder( $orderedIds );

            if ( $result ) {
                wp_send_json_success( [
                    'message' => __( 'Order updated successfully.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
                ] );
            } else {
                wp_send_json_error( [
                    'message' => __( 'Failed to update order.', 'storeinvoice-print-pdf-invoices-and-packing-slips' ),
                ], 500 );
            }
        } catch ( \Exception $e ) {
            // Sanitize exception message before sending to client
            wp_send_json_error( [
                'message' => \sanitize_text_field( $e->getMessage() ),
            ], 500 );
        }
    }

    /**
     * Clear all registrations (useful for testing)
     */
    public static function clear() {
        self::$dataSources = [];
        self::$configs = [];
    }
}
