<?php
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}

/**
 * Return the home .htaccess path using WordPress helpers when available.
 */
function vulnity_uninstall_get_htaccess_path() {
    if (!function_exists('get_home_path')) {
        $file_include = ABSPATH . 'wp-admin/includes/file.php';
        if (file_exists($file_include)) {
            require_once $file_include;
        }
    }

    $home_path = function_exists('get_home_path') ? get_home_path() : ABSPATH;
    return trailingslashit($home_path) . '.htaccess';
}

/**
 * Get an initialized WP_Filesystem instance when possible.
 *
 * @return object|false
 */
function vulnity_uninstall_get_wp_filesystem() {
    global $wp_filesystem;

    if (is_object($wp_filesystem) && method_exists($wp_filesystem, 'delete')) {
        return $wp_filesystem;
    }

    if (!function_exists('WP_Filesystem')) {
        $file_include = ABSPATH . 'wp-admin/includes/file.php';
        if (file_exists($file_include)) {
            require_once $file_include;
        }
    }

    if (function_exists('WP_Filesystem')) {
        WP_Filesystem(false, '', true);
        if (is_object($wp_filesystem) && method_exists($wp_filesystem, 'delete')) {
            return $wp_filesystem;
        }
    }

    return false;
}

/**
 * WordPress-aware writable check for files/dirs.
 *
 * @param string $path Path to check.
 *
 * @return bool
 */
function vulnity_uninstall_is_writable($path) {
    if (!is_string($path) || $path === '') {
        return false;
    }

    if (function_exists('wp_is_writable')) {
        return wp_is_writable($path);
    }

    $wp_filesystem = vulnity_uninstall_get_wp_filesystem();
    if ($wp_filesystem && method_exists($wp_filesystem, 'is_writable')) {
        return (bool) $wp_filesystem->is_writable($path);
    }

    return false;
}

/**
 * Best-effort chmod through WP_Filesystem.
 *
 * @param string $path Path to chmod.
 * @param int    $mode Target mode.
 *
 * @return void
 */
function vulnity_uninstall_maybe_chmod($path, $mode) {
    if (!is_string($path) || $path === '') {
        return;
    }

    $wp_filesystem = vulnity_uninstall_get_wp_filesystem();
    if ($wp_filesystem && method_exists($wp_filesystem, 'chmod')) {
        $wp_filesystem->chmod($path, (int) $mode);
    }
}

/**
 * Delete a path using WordPress file APIs.
 *
 * @param string     $path      Path to delete.
 * @param bool       $recursive Recursive delete when supported.
 * @param bool|mixed $type      Optional type hint for WP_Filesystem::delete.
 *
 * @return void
 */
function vulnity_uninstall_delete_path($path, $recursive = false, $type = false) {
    if (!is_string($path) || $path === '') {
        return;
    }

    if (is_file($path) || is_link($path)) {
        if (function_exists('wp_delete_file')) {
            wp_delete_file($path);
            return;
        }

        $wp_filesystem = vulnity_uninstall_get_wp_filesystem();
        if ($wp_filesystem && method_exists($wp_filesystem, 'delete')) {
            $wp_filesystem->delete($path, false, 'f');
        }
        return;
    }

    $wp_filesystem = vulnity_uninstall_get_wp_filesystem();
    if ($wp_filesystem && method_exists($wp_filesystem, 'delete')) {
        $wp_filesystem->delete($path, (bool) $recursive, $type);
    }
}

/**
 * Remove a plugin marker block from .htaccess without touching unrelated rules.
 */
function vulnity_uninstall_remove_htaccess_marker($marker) {
    if (!is_string($marker) || $marker === '') {
        return;
    }

    $htaccess_path = vulnity_uninstall_get_htaccess_path();
    if (
        !$htaccess_path ||
        !file_exists($htaccess_path) ||
        !is_readable($htaccess_path) ||
        !vulnity_uninstall_is_writable($htaccess_path)
    ) {
        return;
    }

    $contents = file_get_contents($htaccess_path);
    if ($contents === false) {
        return;
    }

    list($updated, $changed) = vulnity_uninstall_strip_marker_blocks_from_contents($contents, $marker);
    if ($changed) {
        file_put_contents($htaccess_path, $updated, LOCK_EX);
    }
}

/**
 * Strip all marker blocks for a marker from content without touching other lines.
 *
 * @param string $contents File content.
 * @param string $marker   Marker name (without BEGIN/END).
 *
 * @return array{0:string,1:bool}
 */
function vulnity_uninstall_strip_marker_blocks_from_contents($contents, $marker) {
    if (!is_string($contents) || !is_string($marker) || $marker === '') {
        return array((string) $contents, false);
    }

    $begin_marker = '# BEGIN ' . $marker;
    $end_marker = '# END ' . $marker;
    $changed = false;
    $offset = 0;

    while (($begin_pos = strpos($contents, $begin_marker, $offset)) !== false) {
        $prefix = substr($contents, 0, $begin_pos);
        $line_start = strrpos($prefix, "\n");
        $line_start = ($line_start === false) ? 0 : ($line_start + 1);

        $end_pos = strpos($contents, $end_marker, $begin_pos);
        if ($end_pos === false) {
            break;
        }

        $line_end = strpos($contents, "\n", $end_pos);
        $line_end = ($line_end === false) ? strlen($contents) : ($line_end + 1);

        $contents = substr($contents, 0, $line_start) . substr($contents, $line_end);
        $changed = true;
        $offset = $line_start;
    }

    return array($contents, $changed);
}

/**
 * Delete a file if it exists and is writable.
 */
function vulnity_uninstall_delete_file($path) {
    if (!is_string($path) || $path === '') {
        return;
    }

    if (!file_exists($path)) {
        return;
    }

    if (!vulnity_uninstall_is_writable($path)) {
        vulnity_uninstall_maybe_chmod($path, 0644);
    }

    vulnity_uninstall_delete_path($path, false, 'f');
}

/**
 * Recursively delete a directory tree for plugin-owned folders.
 */
function vulnity_uninstall_delete_dir_tree($dir) {
    if (!is_string($dir) || $dir === '') {
        return;
    }

    if (!file_exists($dir)) {
        return;
    }

    if (is_link($dir) || is_file($dir)) {
        vulnity_uninstall_delete_file($dir);
        return;
    }

    if (!is_dir($dir)) {
        return;
    }

    $items = scandir($dir);
    if (!is_array($items)) {
        return;
    }

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }

        $path = $dir . '/' . $item;
        if (is_link($path) || is_file($path)) {
            vulnity_uninstall_delete_file($path);
            continue;
        }

        if (is_dir($path)) {
            vulnity_uninstall_delete_dir_tree($path);
        }
    }

    if (!vulnity_uninstall_is_writable($dir)) {
        vulnity_uninstall_maybe_chmod($dir, 0755);
    }

    vulnity_uninstall_delete_path($dir, false, 'd');
}

/**
 * Remove firewall state files created by Vulnity.
 */
function vulnity_uninstall_remove_firewall_files() {
    $candidate_dirs = array();

    if (function_exists('wp_upload_dir')) {
        $uploads = wp_upload_dir(null, false);
        if (is_array($uploads) && !empty($uploads['basedir'])) {
            $candidate_dirs[] = trailingslashit($uploads['basedir']) . 'vulnity-firewall';
        }
    }

    if (defined('WP_CONTENT_DIR')) {
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'vulnity-data/firewall';
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'uploads/vulnity-firewall';
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'vulnity-firewall';
    }

    if (defined('ABSPATH')) {
        $candidate_dirs[] = trailingslashit(ABSPATH) . 'wp-content/vulnity-data/firewall';
    }

    $candidate_dirs = array_values(array_unique(array_filter($candidate_dirs, 'is_string')));

    foreach ($candidate_dirs as $dir) {
        $dir = untrailingslashit($dir);
        vulnity_uninstall_delete_dir_tree($dir);
    }
}

/**
 * Remove Vulnity rotating log files.
 */
function vulnity_uninstall_remove_log_files() {
    $candidate_dirs = array();

    if (function_exists('wp_upload_dir')) {
        $uploads = wp_upload_dir(null, false);
        if (is_array($uploads) && !empty($uploads['basedir']) && is_string($uploads['basedir'])) {
            $candidate_dirs[] = trailingslashit($uploads['basedir']) . 'vulnity-logs';
        }
    }

    if (defined('WP_CONTENT_DIR')) {
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'vulnity-data/logs';
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'vulnity-data';
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'uploads/vulnity-logs';
        $candidate_dirs[] = trailingslashit(WP_CONTENT_DIR) . 'vulnity-logs';
    }

    if (defined('ABSPATH')) {
        $candidate_dirs[] = trailingslashit(ABSPATH) . 'wp-content/vulnity-data/logs';
        $candidate_dirs[] = trailingslashit(ABSPATH) . 'wp-content/vulnity-data';
        $candidate_dirs[] = trailingslashit(ABSPATH) . 'wp-content/vulnity-logs';
        $candidate_dirs[] = trailingslashit(ABSPATH) . 'wp-content/uploads/vulnity-logs';
    }

    $candidate_dirs = array_values(array_unique(array_filter($candidate_dirs, 'is_string')));

    foreach ($candidate_dirs as $dir) {
        $dir = untrailingslashit($dir);
        vulnity_uninstall_delete_dir_tree($dir);
    }
}

/**
 * Delete all options in current site that start with vulnity_.
 */
function vulnity_uninstall_delete_prefixed_options() {
    global $wpdb;
    if (!isset($wpdb) || !($wpdb instanceof wpdb)) {
        return;
    }

    $pattern = $wpdb->esc_like('vulnity_') . '%';
    $option_names = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup requires direct table scan.
        $wpdb->prepare(
            "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
            $pattern
        )
    );

    if (!is_array($option_names)) {
        return;
    }

    foreach ($option_names as $option_name) {
        if (is_string($option_name) && $option_name !== '') {
            delete_option($option_name);
        }
    }
}

/**
 * Remove transient rows that belong to Vulnity for current site.
 */
function vulnity_uninstall_delete_prefixed_transients() {
    global $wpdb;
    if (!isset($wpdb) || !($wpdb instanceof wpdb)) {
        return;
    }

    $transient_patterns = array(
        $wpdb->esc_like('_transient_vulnity_') . '%',
        $wpdb->esc_like('_transient_timeout_vulnity_') . '%',
    );

    foreach ($transient_patterns as $pattern) {
        $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup requires direct delete by wildcard.
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
                $pattern
            )
        );
    }
}

/**
 * Cleanup current blog data.
 */
function vulnity_uninstall_cleanup_current_site() {
    // Delete known plugin options.
    $option_keys = array(
        'vulnity_config',
        'vulnity_alerts',
        'vulnity_alerts_unread',
        'vulnity_retry_queue',
        'vulnity_failed_alerts',
        'vulnity_scanner_log',
        'vulnity_alert_settings',
        'vulnity_blocked_ips',
        'vulnity_ip_whitelist',
        'vulnity_inventory_summary',
        'vulnity_last_sync',
        'vulnity_previous_state',
        'vulnity_last_config_sync',
        'vulnity_last_mitigation_sync',
        'vulnity_mitigation_rules',
        'vulnity_pending_block_notices',
        'vulnity_critical_events_count',
        'vulnity_lockdown_mode',
        'vulnity_compromised',
        'vulnity_heartbeat_status',
        'vulnity_hardening_settings',
        'vulnity_login_htaccess_sync_signature',
        'vulnity_login_route_verified_signature',
        'vulnity_common_paths_htaccess_sync_signature',
        'vulnity_block_transients',
        'vulnity_suppress_blocked_alerts',
    );

    foreach ($option_keys as $option_key) {
        delete_option($option_key);
    }

    // Then clean any residual option/transient by prefix.
    vulnity_uninstall_delete_prefixed_options();
    vulnity_uninstall_delete_prefixed_transients();
}

// Clear scheduled hooks.
$vulnity_cron_hooks = array(
    'vulnity_sync_inventory',
    'vulnity_sync_inventory_deferred',
    'vulnity_process_retry_queue',
    'vulnity_flush_alert_buffer',
    'vulnity_check_heartbeat',
    'vulnity_sync_mitigation_config',
    'vulnity_cleanup_flood_data',
    'vulnity_process_brute_force_windows',
    'vulnity_triggered_inventory_sync',
);

foreach ($vulnity_cron_hooks as $vulnity_hook) {
    wp_unschedule_hook($vulnity_hook);
}

// Cleanup options/transients in current site or all sites on multisite.
if (is_multisite() && function_exists('get_sites')) {
    $vulnity_site_ids = get_sites(array('fields' => 'ids', 'number' => 0));
    if (is_array($vulnity_site_ids)) {
        foreach ($vulnity_site_ids as $vulnity_site_id) {
            switch_to_blog((int) $vulnity_site_id);
            vulnity_uninstall_cleanup_current_site();
            restore_current_blog();
        }
    }
} else {
    vulnity_uninstall_cleanup_current_site();
}

// Cleanup network-level options (multisite).
if (is_multisite() && function_exists('delete_site_option')) {
    global $wpdb;
    if (isset($wpdb) && $wpdb instanceof wpdb && !empty($wpdb->sitemeta)) {
        $vulnity_site_option_pattern = $wpdb->esc_like('vulnity_') . '%';
        $vulnity_site_option_names = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup requires direct network meta scan.
            $wpdb->prepare(
                "SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s",
                $vulnity_site_option_pattern
            )
        );

        if (is_array($vulnity_site_option_names)) {
            foreach ($vulnity_site_option_names as $vulnity_site_option_name) {
                if (is_string($vulnity_site_option_name) && $vulnity_site_option_name !== '') {
                    delete_site_option($vulnity_site_option_name);
                }
            }
        }
    }
}

// Remove only plugin marker blocks from .htaccess.
vulnity_uninstall_remove_htaccess_marker('Vulnity Login URL');
vulnity_uninstall_remove_htaccess_marker('Vulnity Common Paths');
vulnity_uninstall_remove_htaccess_marker('Vulnity Firewall');

// Remove firewall state files.
vulnity_uninstall_remove_firewall_files();

// Remove plugin-owned rotating logs.
vulnity_uninstall_remove_log_files();

if (function_exists('flush_rewrite_rules')) {
    flush_rewrite_rules(false);
}
