<?php
/**
 * Name: Code Snippets Manager
 * Description: Manage reusable CSS, JavaScript, and HTML code snippets to customize site design, add functionality, and inject code without editing theme or plugin files
 * Category: Utilities
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Add Custom Code Without Plugins",
 *     "description": "Inject Google Analytics, Facebook Pixel, or custom CSS without editing theme files or installing separate plugins."
 *   },
 *   {
 *     "title": "Theme-Independent Customization",
 *     "description": "Add custom JavaScript or CSS that survives theme changes without losing your modifications."
 *   },
 *   {
 *     "title": "Quick Design Tweaks",
 *     "description": "Override theme CSS for button colors, fonts, or layout without creating child theme for minor changes."
 *   }
 * ]
 * Added: 2025-09-15
 * Tags: code, css, javascript, snippets, customization
 * WhereToFind: {
 *   "location": "Dedicated Page",
 *   "url": "admin.php?page=switchboard-code-snippets-manager",
 *   "instructions": "Manage code snippets from the dedicated admin page accessible from Switchboard sidebar.",
 *   "newTab": false
 * }
 */

// Prevent direct access
if (!defined('ABSPATH')) exit;

class SwitchboardCodeSnippetsManager {

    public function __construct() {
        add_action('wp_head', [$this, 'output_head_snippets'], 999);
        add_action('wp_footer', [$this, 'output_footer_snippets'], 999);
        add_action('admin_head', [$this, 'output_admin_head_snippets'], 999);
        add_action('admin_footer', [$this, 'output_admin_footer_snippets'], 999);

        // Register admin page
        add_action('admin_menu', [$this, 'register_admin_page']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);

        // AJAX handlers
        add_action('wp_ajax_switchboard_save_snippet', [$this, 'ajax_save_snippet']);
        add_action('wp_ajax_switchboard_delete_snippet', [$this, 'ajax_delete_snippet']);
        add_action('wp_ajax_switchboard_toggle_snippet', [$this, 'ajax_toggle_snippet']);
    }

    /**
     * Register dedicated admin page
     */
    public function register_admin_page() {
        add_submenu_page(
            'switchboard',
            __('Code Snippets Manager', 'wpswitchboard'),
            __('Code Snippets', 'wpswitchboard'),
            'manage_options',
            'switchboard-code-snippets-manager',
            [$this, 'render_admin_page']
        );
    }

    /**
     * Enqueue admin assets
     */
    public function enqueue_admin_assets($hook) {
        if (strpos($hook, 'switchboard-code-snippets-manager') === false) {
            return;
        }

        // Enqueue main Switchboard admin CSS for module classes
        wp_enqueue_style(
            'switchboard-admin',
            plugins_url('../../switchboard-admin.css', __FILE__),
            [],
            '1.0.0'
        );

        // Enqueue Code Snippets Manager specific CSS
        wp_enqueue_style(
            'switchboard-csm-admin',
            plugins_url('../../assets/code-snippets-manager/admin.css', __FILE__),
            ['switchboard-admin'],
            '1.0.0'
        );

        wp_enqueue_script(
            'switchboard-csm-admin',
            plugins_url('../../assets/code-snippets-manager/admin.js', __FILE__),
            ['jquery'],
            '1.0.0',
            true
        );

        wp_localize_script('switchboard-csm-admin', 'switchboard_csm_admin', [
            'nonce' => wp_create_nonce('switchboard_csm_nonce'),
            'ajaxurl' => admin_url('admin-ajax.php'),
            'dashboardUrl' => admin_url('admin.php?page=switchboard-code-snippets-manager'),
        ]);
    }

    /**
     * Render dedicated admin page
     */
    public function render_admin_page() {
        $snippets = $this->get_snippets();
        include plugin_dir_path(__FILE__) . '../../assets/code-snippets-manager/admin.php';
    }

    /**
     * AJAX: Save snippet
     */
    public function ajax_save_snippet() {
        check_ajax_referer('switchboard_csm_nonce', 'nonce');

        // SECURITY FIX: Require unfiltered_html capability for JS/CSS snippets
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Insufficient permissions']);
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below per field
        $snippet_data = isset($_POST['snippet_data']) ? wp_unslash($_POST['snippet_data']) : [];
        $is_edit = isset($_POST['is_edit']) && $_POST['is_edit'];

        if (empty($snippet_data['id']) || empty($snippet_data['title']) || empty($snippet_data['code'])) {
            wp_send_json_error(['message' => 'Missing required fields']);
        }

        // SECURITY FIX: Require unfiltered_html for JS/HTML snippets
        $type = isset($snippet_data['type']) ? $snippet_data['type'] : '';
        if (in_array($type, ['js', 'html'], true) && !current_user_can('unfiltered_html')) {
            wp_send_json_error(['message' => 'JavaScript and HTML snippets require unfiltered_html capability. Please contact your administrator.']);
        }

        // SECURITY FIX: Sanitize snippet data
        $snippet_data['id'] = sanitize_key($snippet_data['id']);
        $snippet_data['title'] = sanitize_text_field($snippet_data['title']);
        $snippet_data['description'] = isset($snippet_data['description']) ? sanitize_textarea_field($snippet_data['description']) : '';
        $snippet_data['type'] = in_array($type, ['css', 'js', 'html'], true) ? $type : 'css';
        $snippet_data['location'] = isset($snippet_data['location']) && in_array($snippet_data['location'], ['header', 'footer'], true) ? $snippet_data['location'] : 'header';
        $snippet_data['priority'] = isset($snippet_data['priority']) ? absint($snippet_data['priority']) : 10;
        $snippet_data['active'] = !empty($snippet_data['active']);
        $snippet_data['created'] = isset($snippet_data['created']) ? sanitize_text_field($snippet_data['created']) : current_time('mysql');
        $snippet_data['modified'] = current_time('mysql');
        // Note: Code is NOT sanitized here as it's meant to be custom code
        // Security is enforced via capability checks above

        $snippets = $this->get_snippets();
        $snippets[$snippet_data['id']] = $snippet_data;

        update_option('switchboard_code_snippets', $snippets);

        wp_send_json_success([
            'message' => 'Snippet saved successfully',
            'redirect_url' => admin_url('admin.php?page=switchboard-code-snippets-manager&action=edit&snippet_id=' . urlencode($snippet_data['id']))
        ]);
    }

    /**
     * AJAX: Delete snippet
     */
    public function ajax_delete_snippet() {
        check_ajax_referer('switchboard_csm_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Insufficient permissions']);
        }

        $snippet_id = isset($_POST['snippet_id']) ? sanitize_key($_POST['snippet_id']) : '';

        if (empty($snippet_id)) {
            wp_send_json_error(['message' => 'Invalid snippet ID']);
        }

        $snippets = $this->get_snippets();

        if (!isset($snippets[$snippet_id])) {
            wp_send_json_error(['message' => 'Snippet not found']);
        }

        unset($snippets[$snippet_id]);
        update_option('switchboard_code_snippets', $snippets);

        wp_send_json_success([
            'message' => 'Snippet deleted successfully',
            'redirect_url' => admin_url('admin.php?page=switchboard-code-snippets-manager')
        ]);
    }

    /**
     * AJAX: Toggle snippet active status
     */
    public function ajax_toggle_snippet() {
        check_ajax_referer('switchboard_csm_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Insufficient permissions']);
        }

        $snippet_id = isset($_POST['snippet_id']) ? sanitize_key($_POST['snippet_id']) : '';
        $active = isset($_POST['active']) && $_POST['active'] === 'true';

        if (empty($snippet_id)) {
            wp_send_json_error(['message' => 'Invalid snippet ID']);
        }

        $snippets = $this->get_snippets();

        if (!isset($snippets[$snippet_id])) {
            wp_send_json_error(['message' => 'Snippet not found']);
        }

        // Update active status
        $snippets[$snippet_id]['active'] = $active;
        $snippets[$snippet_id]['modified'] = current_time('mysql');

        update_option('switchboard_code_snippets', $snippets);

        wp_send_json_success([
            'message' => $active ? 'Snippet activated' : 'Snippet deactivated',
            'active' => $active
        ]);
    }

    private function get_snippets() {
        $snippets = get_option('switchboard_code_snippets', []);
        return is_array($snippets) ? $snippets : [];
    }

    private function get_sorted_snippets() {
        $snippets = $this->get_snippets();
        // Sort by priority (lower = earlier)
        uasort($snippets, function($a, $b) {
            $priority_a = isset($a['priority']) ? (int) $a['priority'] : 10;
            $priority_b = isset($b['priority']) ? (int) $b['priority'] : 10;
            return $priority_a - $priority_b;
        });
        return $snippets;
    }
    

    private function output_snippet($snippet, $hook_location) {
        if (empty($snippet['active'])) {
            return;
        }

        $location = isset($snippet['location']) ? $snippet['location'] : '';
        $type = isset($snippet['type']) ? $snippet['type'] : '';
        $code = isset($snippet['code']) ? $snippet['code'] : '';

        // Map our location to hook location
        $location_map = [
            'header' => 'header',
            'footer' => 'footer',
        ];

        if (!isset($location_map[$location]) || $location_map[$location] !== $hook_location) {
            return;
        }

        echo "\n<!-- Switchboard Snippet: " . esc_html($snippet['title']) . " -->\n";

        // SECURITY NOTE: Code snippets are a powerful feature intended for administrators only
        // Only users with 'unfiltered_html' capability should be able to create snippets
        // The code is output as-is because it's meant to add custom functionality
        // However, we add warning comments to remind admins to be careful

        if ($type === 'css') {
            echo '<style type="text/css">' . "\n";
            // SECURITY FIX: Strip dangerous elements while preserving CSS
            // Remove script tags, event handlers, and javascript: protocols
            $code = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $code);
            $code = preg_replace('/on\w+\s*=\s*["\'][^"\']*["\']/i', '', $code);
            $code = preg_replace('/javascript:/i', '', $code);
            // Note: CSS is output directly as this is an admin feature for custom styling
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Admin-created custom CSS
            echo $code;
            echo "\n</style>\n";
        } elseif ($type === 'js') {
            echo '<script type="text/javascript">' . "\n";
            // SECURITY FIX: Validate that user has unfiltered_html capability
            // This prevents lower-privilege users from injecting malicious JavaScript
            if (!current_user_can('unfiltered_html')) {
                echo '// JavaScript snippets disabled - insufficient permissions';
            } else {
                // Remove potentially dangerous script wrapper tags
                $code = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '$1', $code);
                // Note: JavaScript is output directly as this is an admin feature for custom functionality
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Admin-created custom JavaScript
                echo $code;
            }
            echo "\n</script>\n";
        } elseif ($type === 'html') {
            // SECURITY FIX: Validate that user has unfiltered_html capability
            // This prevents lower-privilege users from injecting malicious HTML
            if (!current_user_can('unfiltered_html')) {
                echo '<!-- HTML snippets disabled - insufficient permissions -->';
            } else {
                // Note: HTML is output directly as this is an admin feature for custom functionality
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Admin-created custom HTML
                echo $code . "\n";
            }
        }
    }

    public function output_head_snippets() {
        $snippets = $this->get_sorted_snippets();
        foreach ($snippets as $snippet) {
            $this->output_snippet($snippet, 'header');
        }
    }

    public function output_footer_snippets() {
        $snippets = $this->get_sorted_snippets();
        foreach ($snippets as $snippet) {
            $this->output_snippet($snippet, 'footer');
        }
    }

    public function output_admin_head_snippets() {
        // Admin snippets handled same as frontend for now
    }

    public function output_admin_footer_snippets() {
        // Admin snippets handled same as frontend for now
    }


    /**
     * Get module metadata for data registry
     */
    public function get_data_schema() {
        return [
            'options' => ['switchboard_code_snippets'],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

// Initialize and register with data system
$switchboard_instance = new SwitchboardCodeSnippetsManager();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
