<?php
// Security: Prevent direct access to plugin files.
defined( 'ABSPATH' ) || exit;

// Load premium methods trait (premium version only). Freemius strips this file from the free ZIP automatically.
if ( file_exists( __DIR__ . '/class-iotc-actions-premium__premium_only.php' ) ) {
    require_once __DIR__ . '/class-iotc-actions-premium__premium_only.php';
}

// Fallback empty trait for free version (when premium file is absent).
if ( ! trait_exists( 'IOTC_Actions_Premium' ) ) {
    trait IOTC_Actions_Premium {}
}

class IOTC_Actions {

    use IOTC_Actions_Premium;

    public function __construct() {
        add_action( 'admin_init', array( $this, 'handle_actions' ) );
        add_action( 'wp_ajax_iotc_trash_orphan_table', array( $this, 'ajax_trash_orphan_table' ) );
        add_action( 'wp_ajax_iotc_restore_orphan_table', array( $this, 'ajax_restore_orphan_table' ) );
        add_action( 'wp_ajax_iotc_force_delete_orphan_table', array( $this, 'ajax_force_delete_orphan_table' ) );
        
        // Premium Bulk actions (registered only when Pro trait exists)
        if ( method_exists( $this, 'ajax_bulk_trash_orphan_tables__premium_only' ) ) {
            add_action( 'wp_ajax_iotc_bulk_trash_orphan_tables__premium_only', array( $this, 'ajax_bulk_trash_orphan_tables__premium_only' ) );
            add_action( 'wp_ajax_iotc_bulk_restore_orphan_tables__premium_only', array( $this, 'ajax_bulk_restore_orphan_tables__premium_only' ) );
            add_action( 'wp_ajax_iotc_bulk_force_delete_orphan_tables__premium_only', array( $this, 'ajax_bulk_force_delete_orphan_tables__premium_only' ) );
            add_action( 'wp_ajax_iotc_save_cron_settings', array( $this, 'ajax_save_cron_settings__premium_only' ) );
        }

        // Optimization (Free)
        add_action( 'wp_ajax_iotc_optimize_database', array( $this, 'ajax_optimize_database' ) );

        // Emergency restore via AJAX (token-authenticated, no login required).
        add_action( 'wp_ajax_iotc_emergency_restore', array( $this, 'ajax_emergency_restore' ) );
        add_action( 'wp_ajax_nopriv_iotc_emergency_restore', array( $this, 'ajax_emergency_restore' ) );
    }

    /**
     * Handle synchronous table actions (ignore, reset, backup).
     */
    public function handle_actions() {
        if ( ! isset( $_GET['action'], $_GET['_wpnonce'] ) ) {
            return;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) );
        }

        $action = sanitize_text_field( wp_unslash( $_GET['action'] ) );

        // Handle Ignore Action
        if ( 'ignore_table' === $action && isset( $_GET['table'] ) ) {
            if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'ignore_table_nonce' ) ) {
                wp_die( esc_html__( 'Security check failed.', 'intouch-orphan-table-cleaner' ) );
            }

            $table_to_ignore = sanitize_text_field( wp_unslash( $_GET['table'] ) );
            $ignored_tables = get_option( 'iotc_ignored_tables', array() );
            
            if ( ! in_array( $table_to_ignore, $ignored_tables, true ) ) {
                $ignored_tables[] = $table_to_ignore;
                update_option( 'iotc_ignored_tables', $ignored_tables );
            }

            add_settings_error(
                'iotc_messages',
                'table_action',
                sprintf(
                    /* translators: %s: Table name */
                    __( 'Table %s has been added to the ignored list.', 'intouch-orphan-table-cleaner' ),
                    esc_html( $table_to_ignore )
                ),
                'success'
            );
        }

        // Handle Reset Ignored Action
        if ( 'reset_ignored' === $action ) {
            if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'reset_ignored_nonce' ) ) {
                wp_die( esc_html__( 'Security check failed.', 'intouch-orphan-table-cleaner' ) );
            }

            delete_option( 'iotc_ignored_tables' );

            add_settings_error(
                'iotc_messages',
                'table_action',
                __( 'Ignored tables list has been reset.', 'intouch-orphan-table-cleaner' ),
                'success'
            );
        }

        // Handle Single Table Backup Action (Free)
        if ( 'backup_table' === $action && isset( $_GET['table'] ) ) {
            $this->backup_table_action();
        }

        // Handle Full DB Backup Action (Pro only)
        if ( 'backup_full_db' === $action ) {
            if ( method_exists( $this, 'backup_full_db_action__premium_only' ) ) {
                $this->backup_full_db_action__premium_only();
            } else {
                wp_die( esc_html__( 'Full Database Backup is a Pro feature.', 'intouch-orphan-table-cleaner' ) );
            }
        }
    }

    /**
     * AJAX handler for moving a table to trash (Dry Run - Free).
     */
    public function ajax_trash_orphan_table() {
        check_ajax_referer( 'ajax_trash_table_nonce', 'nonce' );

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) ) );
        }

        if ( ! isset( $_POST['table_name'] ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        global $wpdb;
        $table_to_trash = sanitize_text_field( wp_unslash( $_POST['table_name'] ) );

        if ( strpos( $table_to_trash, $wpdb->prefix ) !== 0 ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        // Rename table to simulate deletion
        $trashed_name = $table_to_trash . '_iotc_trash';
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
        $result = $wpdb->query( $wpdb->prepare( "RENAME TABLE %i TO %i", $table_to_trash, $trashed_name ) );

        if ( $result === false ) {
            /* translators: %1$s: DB Error, %2$s: SQL Query */
            $error_msg = sprintf( __( 'Error renaming table. DB Error: %1$s. Query: %2$s', 'intouch-orphan-table-cleaner' ), $wpdb->last_error, $wpdb->last_query );
            wp_send_json_error( array(
                'message' => $error_msg
            ) );
        }

        /* translators: %s: dynamic value */
        $success_msg = sprintf( __( 'Table %s successfully suspended.', 'intouch-orphan-table-cleaner' ), esc_html( $table_to_trash ) );
        wp_send_json_success( array(
            'message' => $success_msg
        ) );
    }

    /**
     * AJAX handler for restoring a trashed table (Free).
     */
    public function ajax_restore_orphan_table() {
        check_ajax_referer( 'ajax_restore_table_nonce', 'nonce' );

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) ) );
        }

        if ( ! isset( $_POST['table_name'] ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        global $wpdb;
        $trashed_name = sanitize_text_field( wp_unslash( $_POST['table_name'] ) );

        if ( strpos( $trashed_name, $wpdb->prefix ) !== 0 || substr( $trashed_name, -11 ) !== '_iotc_trash' ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        $original_name = substr( $trashed_name, 0, -11 );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
        $result = $wpdb->query( $wpdb->prepare( "RENAME TABLE %i TO %i", $trashed_name, $original_name ) );

        if ( $result === false ) {
            wp_send_json_error( array( 'message' => __( 'Error restoring table.', 'intouch-orphan-table-cleaner' ) ) );
        }

        /* translators: %s: dynamic value */
        $success_msg = sprintf( __( 'Table %s successfully restored.', 'intouch-orphan-table-cleaner' ), esc_html( $original_name ) );
        wp_send_json_success( array(
            'message' => $success_msg
        ) );
    }

    /**
     * AJAX handler for permanently deleting a trashed table (Free).
     */
    public function ajax_force_delete_orphan_table() {
        check_ajax_referer( 'ajax_force_delete_table_nonce', 'nonce' );

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) ) );
        }

        if ( ! isset( $_POST['table_name'] ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        global $wpdb;
        $table_to_drop = sanitize_text_field( wp_unslash( $_POST['table_name'] ) );

        if ( strpos( $table_to_drop, $wpdb->prefix ) !== 0 || substr( $table_to_drop, -11 ) !== '_iotc_trash' ) {
            wp_send_json_error( array( 'message' => __( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) ) );
        }

        // Get table size before deletion
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $table_to_drop ) );
        $size_mb = 0;
        if ( $status ) {
            $size_mb = round( ($status->Data_length + $status->Index_length) / 1048576, 2 );
        }

        // Execute deletion
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
        if ( $wpdb->query( $wpdb->prepare( "DROP TABLE IF EXISTS %i", $table_to_drop ) ) !== false ) {
            // Update stats
            update_option( 'iotc_total_tables_deleted', (int) get_option( 'iotc_total_tables_deleted', 0 ) + 1 );
            update_option( 'iotc_total_space_freed_mb', round( (float) get_option( 'iotc_total_space_freed_mb', 0 ) + $size_mb, 2 ) );
        }
        
        // Remove from ignored list if original was there
        $original_name = substr( $table_to_drop, 0, -11 );
        $ignored_tables = get_option( 'iotc_ignored_tables', array() );
        if ( ( $key = array_search( $original_name, $ignored_tables, true ) ) !== false ) {
            unset( $ignored_tables[ $key ] );
            update_option( 'iotc_ignored_tables', $ignored_tables );
        }

        /* translators: %s: dynamic value */
        $success_msg = sprintf( __( 'Table %s permanently deleted.', 'intouch-orphan-table-cleaner' ), esc_html( $table_to_drop ) );
        wp_send_json_success( array(
            'message' => $success_msg
        ) );
    }

    /**
     * AJAX handler for database optimization (OPTIMIZE TABLE - Free).
     */
    public function ajax_optimize_database() {
        check_ajax_referer( 'ajax_optimize_database_nonce', 'nonce' );

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) ) );
        }

        global $wpdb;
        $prefix = $wpdb->prefix;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $tables = $wpdb->get_col( $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $prefix ) . '%' ) );

        if ( empty( $tables ) ) {
            wp_send_json_error( array( 'message' => __( 'No tables found to optimize.', 'intouch-orphan-table-cleaner' ) ) );
        }

        $optimized_count = 0;
        foreach ( $tables as $table ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
            if ( $wpdb->query( $wpdb->prepare( "OPTIMIZE TABLE %i", $table ) ) !== false ) {
                $optimized_count++;
                // InnoDB's Data_free (overhead) stat isn't refreshed by OPTIMIZE TABLE alone;
                // ANALYZE TABLE forces MySQL to recalculate it so the UI reflects the real value.
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
                $wpdb->query( $wpdb->prepare( "ANALYZE TABLE %i", $table ) );
            }
        }

        /* translators: %d: number of tables */
        $success_msg = sprintf( __( '%d database tables optimized successfully.', 'intouch-orphan-table-cleaner' ), $optimized_count );
        wp_send_json_success( array(
            'message' => $success_msg
        ) );
    }

    /**
     * Handle single-table backup download via direct HTTP stream (Free).
     */
    public function backup_table_action() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to perform this action.', 'intouch-orphan-table-cleaner' ) );
        }

        if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'backup_table_nonce' ) ) {
            wp_die( esc_html__( 'Security check failed.', 'intouch-orphan-table-cleaner' ) );
        }

        global $wpdb;
        $table_to_backup = isset( $_GET['table'] ) ? sanitize_text_field( wp_unslash( $_GET['table'] ) ) : '';

        if ( '' === $table_to_backup || strpos( $table_to_backup, $wpdb->prefix ) !== 0 ) {
            wp_die( esc_html__( 'Invalid table name.', 'intouch-orphan-table-cleaner' ) );
        }

        $filename = 'backup_' . $table_to_backup . '_' . gmdate( 'Y-m-d' ) . '.sql';
        header( 'Content-Type: application/octet-stream' );
        header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
        header( 'Pragma: no-cache' );
        header( 'Expires: 0' );

        echo "-- Backup of table: " . esc_html( $table_to_backup ) . "\n";
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        echo "-- Date: " . gmdate( 'Y-m-d H:i:s' ) . "\n\n";
        // Raw SQL streamed as a downloadable .sql file, not HTML output — esc_html() would corrupt the statement.
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        echo $wpdb->prepare( "DROP TABLE IF EXISTS %i", $table_to_backup ) . ";\n\n";

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
        $create_table = $wpdb->get_row( $wpdb->prepare( "SHOW CREATE TABLE %i", $table_to_backup ), ARRAY_N );
        if ( ! empty( $create_table[1] ) ) {
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $create_table[1] . ";\n\n";
        }

        $offset = 0;
        $limit  = 500;
        while ( true ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.NotPrepared
            $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM %i LIMIT %d, %d", $table_to_backup, $offset, $limit ), ARRAY_A );
            if ( empty( $rows ) ) {
                break;
            }

            foreach ( $rows as $row ) {
                $values = array();
                foreach ( $row as $val ) {
                    if ( null === $val ) {
                        $values[] = "NULL";
                    } else {
                        $values[] = "'" . esc_sql( $val ) . "'";
                    }
                }
                // Each value was already escaped via esc_sql() above; the query template itself is dynamic but safe.
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.DB.PreparedSQL.NotPrepared
                echo $wpdb->prepare( "INSERT INTO %i VALUES(" . implode( ', ', $values ) . ")", $table_to_backup ) . ";\n";
            }
            $offset += $limit;
        }

        echo "\n-- End of backup\n";
        exit;
    }

    /**
     * AJAX handler for emergency recovery (token-authenticated, no login required).
     * Replaces the old standalone emergency-restore.php file.
     * Accessible via: /wp-admin/admin-ajax.php?action=iotc_emergency_restore&token=TOKEN
     */
    public function ajax_emergency_restore() {
        // Token authentication — no nonce because user may be locked out.
        $submitted_token = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $stored_token    = get_option( 'iotc_recovery_token' );

        if ( ! $stored_token || ! hash_equals( $stored_token, $submitted_token ) ) {
            wp_die( 'Security Error: Invalid or missing token.', 403 );
        }

        global $wpdb;

        // Handle POST: perform the actual restoration.
        if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
            if ( ! isset( $_POST['iotc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['iotc_nonce'] ) ), 'iotc_emergency_restore' ) ) {
                wp_die( 'Security Error: Invalid nonce.' );
            }

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $iotc_tables = $wpdb->get_results( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", '%_iotc_trash' ) );
            $restored    = 0;

            if ( $iotc_tables ) {
                foreach ( $iotc_tables as $t ) {
                    $trashed  = current( (array) $t );
                    $original = substr( $trashed, 0, -11 );
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
                    $wpdb->query( $wpdb->prepare( "RENAME TABLE %i TO %i", $trashed, $original ) );
                    $restored++;
                }
            }

            $css_url = plugins_url( 'assets/css/emergency-restore.css', dirname( dirname( __FILE__ ) ) . '/intouch-orphan-table-cleaner.php' );
            wp_register_style( 'iotc-emergency-restore', $css_url, array(), IOTC_VERSION );
            wp_enqueue_style( 'iotc-emergency-restore' );
            header( 'Content-Type: text/html; charset=UTF-8' );
            echo '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Recovery Complete</title>';
            wp_print_styles( 'iotc-emergency-restore' );
            echo '</head><body><div class="card card--success">';
            echo '<h1 class="success-title">' . esc_html__( 'Restoration Complete!', 'intouch-orphan-table-cleaner' ) . '</h1>';
            echo '<p>' . sprintf(
                /* translators: %d: number of restored tables */
                esc_html__( 'Successfully restored %d suspended table(s).', 'intouch-orphan-table-cleaner' ),
                intval( $restored )
            ) . '</p>';
            $site_url = get_option( 'siteurl' );
            if ( $site_url ) {
                echo '<a href="' . esc_url( $site_url . '/wp-admin/' ) . '" class="button button--primary">' . esc_html__( 'Go back to WordPress Dashboard', 'intouch-orphan-table-cleaner' ) . '</a>';
            }
            echo '</div></body></html>';
            exit;
        }

        // GET: display confirmation UI.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $iotc_tables = $wpdb->get_results( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", '%_iotc_trash' ) );
        $count       = $iotc_tables ? count( $iotc_tables ) : 0;
        $form_url    = admin_url( 'admin-ajax.php?action=iotc_emergency_restore&token=' . rawurlencode( $submitted_token ) );
        $css_url     = plugins_url( 'assets/css/emergency-restore.css', dirname( dirname( __FILE__ ) ) . '/intouch-orphan-table-cleaner.php' );
        wp_register_style( 'iotc-emergency-restore', $css_url, array(), IOTC_VERSION );
        wp_enqueue_style( 'iotc-emergency-restore' );

        header( 'Content-Type: text/html; charset=UTF-8' );
        echo '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">';
        echo '<title>' . esc_html__( 'WP Intouch Orphan Table Cleaner - Emergency Recovery', 'intouch-orphan-table-cleaner' ) . '</title>';
        wp_print_styles( 'iotc-emergency-restore' );
        echo '</head><body><div class="card">';
        echo '<h1>' . esc_html__( 'Emergency Recovery Console', 'intouch-orphan-table-cleaner' ) . '</h1>';

        if ( $count > 0 ) {
            echo '<p>' . sprintf(
                /* translators: %d: number of suspended tables */
                esc_html__( 'We found %d suspended table(s) in your database.', 'intouch-orphan-table-cleaner' ),
                intval( $count )
            ) . '</p>';
            echo '<div class="warning-box"><p class="warning-box-text">';
            echo '<strong>' . esc_html__( 'Did your site crash?', 'intouch-orphan-table-cleaner' ) . '</strong><br>';
            echo esc_html__( 'If your site stopped working after suspending an orphan table, click the button below. We will instantly restore all suspended tables to fix the fatal error.', 'intouch-orphan-table-cleaner' );
            echo '</p></div>';
            echo '<form method="POST" action="' . esc_url( $form_url ) . '">';
            wp_nonce_field( 'iotc_emergency_restore', 'iotc_nonce' );
            echo '<input type="hidden" name="restore_tables" value="1">';
            echo '<button type="submit" class="button">' . esc_html__( 'Restore All Suspended Tables Now', 'intouch-orphan-table-cleaner' ) . '</button>';
            echo '</form>';
        } else {
            echo '<p class="success">' . esc_html__( 'There are currently no suspended tables to restore. Your database is clean!', 'intouch-orphan-table-cleaner' ) . '</p>';
            $site_url = get_option( 'siteurl' );
            if ( $site_url ) {
                echo '<a href="' . esc_url( $site_url . '/wp-admin/' ) . '" class="dashboard-link">' . esc_html__( 'Return to Dashboard', 'intouch-orphan-table-cleaner' ) . '</a>';
            }
        }

        echo '</div></body></html>';
        exit;
    }
}
