<?php

/**
 * Plugin Name: AnchorKit - Table of Contents
 * Plugin URI: https://getanchorkit.com
 * Description: Modern, accessible Table of Contents plugin with live preview, sticky positioning, scroll-spy, and comprehensive customization. Gutenberg & Elementor ready.
 * Version: 1.0.0
 * Requires at least: 5.0
 * Requires PHP: 7.4
 * Tested up to: 6.9
 * Author: Smelter Studio
 * Author URI: https://smelter.studio
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: anchorkit-table-of-contents
 * Domain Path: /languages
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Define plugin constants
 *
 * ANCHORKIT_PLUGIN_VERSION - Current plugin version, synced with header
 * ANCHORKIT_FREEMIUS_PLAN_FREE - Freemius free plan ID
 * ANCHORKIT_FREEMIUS_PRICE_SINGLE - Freemius pricing ID for single site
 * ANCHORKIT_FREEMIUS_PRICE_TEN_SITE - Freemius pricing ID for 10 sites
 * ANCHORKIT_FREEMIUS_PRICE_UNLIMITED - Freemius pricing ID for unlimited sites
 */
if ( !defined( 'ANCHORKIT_PLUGIN_VERSION' ) ) {
    $anchorkit_plugin_data = get_file_data( __FILE__, array(
        'Version' => 'Version',
    ) );
    define( 'ANCHORKIT_PLUGIN_VERSION', $anchorkit_plugin_data['Version'] ?? '1.0.0' );
}
if ( !defined( 'ANCHORKIT_PLUGIN_FILE' ) ) {
    define( 'ANCHORKIT_PLUGIN_FILE', __FILE__ );
}
if ( !defined( 'ANCHORKIT_FREEMIUS_PLAN_FREE' ) ) {
    define( 'ANCHORKIT_FREEMIUS_PLAN_FREE', 36848 );
}
if ( !defined( 'ANCHORKIT_FREEMIUS_PRICE_SINGLE' ) ) {
    define( 'ANCHORKIT_FREEMIUS_PRICE_SINGLE', 48202 );
}
if ( !defined( 'ANCHORKIT_FREEMIUS_PRICE_TEN_SITE' ) ) {
    define( 'ANCHORKIT_FREEMIUS_PRICE_TEN_SITE', 48203 );
}
if ( !defined( 'ANCHORKIT_FREEMIUS_PRICE_UNLIMITED' ) ) {
    define( 'ANCHORKIT_FREEMIUS_PRICE_UNLIMITED', 48204 );
}
/**
 * Plugin UI/DOM Constants
 *
 * These constants define CSS class names and HTML attributes used throughout
 * the plugin. Centralizing them here prevents typos and makes refactoring easier.
 *
 * @since 1.0.0
 */
if ( !defined( 'ANCHORKIT_TOC_CONTAINER_CLASS' ) ) {
    define( 'ANCHORKIT_TOC_CONTAINER_CLASS', 'anchorkit-toc-container' );
}
if ( !defined( 'ANCHORKIT_TOC_ELEMENTOR_CLASS' ) ) {
    define( 'ANCHORKIT_TOC_ELEMENTOR_CLASS', 'anchorkit-toc-elementor' );
}
if ( !defined( 'ANCHORKIT_TOC_CUSTOM_STYLING_CLASS' ) ) {
    define( 'ANCHORKIT_TOC_CUSTOM_STYLING_CLASS', 'anchorkit-toc-custom-styling' );
}
if ( !defined( 'ANCHORKIT_TOC_INSTANCE_OVERRIDE_CLASS' ) ) {
    define( 'ANCHORKIT_TOC_INSTANCE_OVERRIDE_CLASS', 'anchorkit-toc-instance-override' );
}
if ( !defined( 'ANCHORKIT_TOC_INSTANCE_ATTR' ) ) {
    define( 'ANCHORKIT_TOC_INSTANCE_ATTR', 'data-anchorkit-instance' );
}
if ( !defined( 'ANCHORKIT_TOC_AUTO_THEME_ATTR' ) ) {
    define( 'ANCHORKIT_TOC_AUTO_THEME_ATTR', 'data-anchorkit-auto-theme' );
}
if ( !defined( 'ANCHORKIT_PREVIEW_CONTENT_CLASS' ) ) {
    define( 'ANCHORKIT_PREVIEW_CONTENT_CLASS', 'anchorkit-preview-content' );
}
// Load core initialization early so helpers are available during Freemius hooks
require_once __DIR__ . '/includes/init.php';
add_action( 'anchorkit_fs_loaded', 'anchorkit_register_freemius_hooks' );
/**
 * Freemius SDK bootstrap
 * -----------------------------------------------------------------------------
 * Make sure you have:
 * - vendor/freemius/start.php in your plugin folder
 * - Freemius constants in wp-config.php (WP_FS__anchorkit_SECRET_KEY, etc.)
 */
if ( function_exists( 'anchorkit_fs' ) ) {
    $anchorkit_fs_instance = anchorkit_fs();
    if ( $anchorkit_fs_instance ) {
        // Let Freemius know which file is the free version (needed for wp.org auto-deactivation flow).
        $anchorkit_fs_instance->set_basename( true, __FILE__ );
    }
} else {
    if ( !function_exists( 'anchorkit_fs' ) ) {
        // Create a helper function for easy SDK access.
        function anchorkit_fs() {
            global $anchorkit_fs;
            if ( !isset( $anchorkit_fs ) ) {
                $freemius_bootstrap = __DIR__ . '/vendor/freemius/start.php';
                if ( !file_exists( $freemius_bootstrap ) ) {
                    return null;
                }
                require_once $freemius_bootstrap;
                try {
                    $anchorkit_fs = fs_dynamic_init( array(
                        'id'               => '22053',
                        'slug'             => 'anchorkit',
                        'premium_slug'     => 'anchorkit-pro',
                        'type'             => 'plugin',
                        'public_key'       => 'pk_3bb90ffdcccae3379b9f5213de6ca',
                        'is_premium'       => false,
                        'premium_suffix'   => 'Pro',
                        'has_addons'       => false,
                        'has_paid_plans'   => true,
                        'is_org_compliant' => true,
                        'menu'             => array(
                            'slug'       => 'anchorkit-settings',
                            'first-path' => 'options-general.php?page=anchorkit-settings',
                            'contact'    => false,
                            'support'    => false,
                            'pricing'    => false,
                            'parent'     => array(
                                'slug' => 'options-general.php',
                            ),
                        ),
                        'is_live'          => true,
                    ) );
                } catch ( Throwable $freemius_exception ) {
                    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging only in WP_DEBUG mode
                        error_log( 'AnchorKit Freemius bootstrap failed: ' . $freemius_exception->getMessage() );
                    }
                    $anchorkit_fs = null;
                }
            }
            return $anchorkit_fs;
        }

    }
    // Init Freemius.
    $anchorkit_fs_instance = anchorkit_fs();
    if ( $anchorkit_fs_instance ) {
        // Signal that SDK was initiated.
        do_action( 'anchorkit_fs_loaded' );
    }
}
/**
 * Translations are loaded automatically by WordPress.org for hosted plugins since WP 4.6.
 * No manual load_plugin_textdomain() call is needed.
 *
 * @see https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#loading-text-domain
 */
/**
 * Configure Freemius filters/actions once the SDK finishes booting.
 *
 * SECURITY & BEST PRACTICES REVIEW:
 * ----------------------------------
 * ✓ Text Overrides: Using WordPress i18n functions (__) for translation
 * ✓ XSS Prevention: All user-facing text is escaped via Freemius SDK
 * ✓ Hook Priority: Using standard WordPress hook priorities
 * ✓ Nonce Verification: Handled by Freemius SDK for license changes
 * ✓ Minimal Footprint: Text overrides only, no database modifications here
 *
 * @return void
 */
function anchorkit_register_freemius_hooks() {
    if ( !function_exists( 'anchorkit_fs' ) ) {
        return;
    }
    $fs = anchorkit_fs();
    if ( !$fs ) {
        return;
    }
    $fs->add_filter(
        'connect_message',
        'anchorkit_fs_connect_message',
        10,
        6
    );
    $fs->add_filter(
        'connect_message_on_update',
        'anchorkit_fs_connect_message',
        10,
        6
    );
    // Hide monthly pricing - only show annual prices (no monthly breakdowns or fractions)
    $fs->add_filter( 'pricing/show_annual_in_monthly', '__return_false' );
    // Trials are handled via Freemius dashboard configuration. Until we launch one,
    // explicitly hide any generic prompts.
    $fs->add_filter( 'show_trial', '__return_false' );
    // Customize the upgrade success message (ensuring it works during Free -> Pro transition)
    // We call this on 'init' to avoid "translation loaded too early" warnings in WP 6.7+
    add_action( 'init', function () use($fs) {
        $fs->override_i18n( array(
            'yee-haw'                          => __( 'Welcome to AnchorKit Pro', 'anchorkit-table-of-contents' ),
            'plan-upgraded-message'            => __( 'You can now start using Pro features.', 'anchorkit-table-of-contents' ),
            'plan-activated-message'           => __( 'You can now start using Pro features.', 'anchorkit-table-of-contents' ),
            'follow-steps-to-complete-upgrade' => '',
            'download-latest-x-version'        => '',
            'upload-and-activate'              => '',
            'howto-upload-activate'            => '',
        ) );
    }, 0 );
    // Redirect to settings page after successful upgrade instead of account page
    $fs->add_action(
        'after_license_change',
        'anchorkit_redirect_after_upgrade',
        10,
        2
    );
    // Add custom styling for the upgrade notice
    add_action( 'admin_enqueue_scripts', 'anchorkit_enqueue_upgrade_notice_styles' );
    // Hook uninstall cleanup to Freemius after_uninstall action
    $fs->add_action( 'after_uninstall', 'anchorkit_fs_uninstall_cleanup' );
}

/**
 * Cleanup plugin data on uninstall.
 * Hooked to Freemius 'after_uninstall' action so Freemius can track uninstalls first.
 *
 * @return void
 */
function anchorkit_fs_uninstall_cleanup() {
    $prefixes = array('anchorkit_');
    global $wpdb;
    // Delete options with prefixes
    // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup, caching not appropriate
    foreach ( $prefixes as $prefix ) {
        $like = $wpdb->esc_like( $prefix ) . '%';
        $option_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $like ) );
        foreach ( $option_names as $option_name ) {
            delete_option( $option_name );
        }
    }
    // Delete transients
    $bases = array('_transient_', '_transient_timeout_');
    foreach ( $prefixes as $prefix ) {
        foreach ( $bases as $base ) {
            $like = $wpdb->esc_like( $base . $prefix ) . '%';
            $option_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $like ) );
            foreach ( $option_names as $option_name ) {
                delete_option( $option_name );
            }
        }
    }
    // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    // Multisite cleanup
    if ( is_multisite() ) {
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup, caching not appropriate
        $site_ids = get_sites( array(
            'fields' => 'ids',
        ) );
        foreach ( $site_ids as $site_id ) {
            switch_to_blog( $site_id );
            // Repeat options/transients cleanup for each site
            foreach ( $prefixes as $prefix ) {
                $like = $wpdb->esc_like( $prefix ) . '%';
                $option_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $like ) );
                foreach ( $option_names as $option_name ) {
                    delete_option( $option_name );
                }
            }
            // Transients for this site
            foreach ( $prefixes as $prefix ) {
                foreach ( $bases as $base ) {
                    $like = $wpdb->esc_like( $base . $prefix ) . '%';
                    $option_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $like ) );
                    foreach ( $option_names as $option_name ) {
                        delete_option( $option_name );
                    }
                }
            }
        }
        restore_current_blog();
        // Network options
        foreach ( $prefixes as $prefix ) {
            $like = $wpdb->esc_like( $prefix ) . '%';
            $meta_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s", $like ) );
            foreach ( $meta_keys as $meta_key ) {
                delete_site_option( $meta_key );
            }
        }
        // Network transients
        foreach ( $prefixes as $prefix ) {
            $like = $wpdb->esc_like( '_site_transient_' . $prefix ) . '%';
            $meta_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s", $like ) );
            foreach ( $meta_keys as $meta_key ) {
                $transient = substr( $meta_key, strlen( '_site_transient_' ) );
                delete_site_transient( $transient );
            }
        }
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    }
}

/**
 * Custom opt-in message for clarity around telemetry and upgrades.
 *
 * @param string $message
 * @param string $first_name
 * @param string $product_title
 * @param string $user_login
 * @param string $site_link
 * @param string $terms_html
 * @return string
 */
function anchorkit_fs_connect_message(
    $message,
    $first_name,
    $product_title,
    $user_login,
    $site_link,
    $terms_html
) {
    if ( $first_name ) {
        // translators: %s is the user's first name
        $greeting = sprintf( esc_html__( 'Hi %s - thanks for installing AnchorKit!', 'anchorkit-table-of-contents' ), esc_html( $first_name ) );
    } else {
        $greeting = esc_html__( 'Hi there - thanks for installing AnchorKit!', 'anchorkit-table-of-contents' );
    }
    $privacy = esc_html__( 'Opt in to receive release & security updates and to unlock one-click upgrades inside the dashboard. We only collect your site URL, WordPress version, and email for license management - never your front-end content or visitor data.', 'anchorkit-table-of-contents' );
    $control = esc_html__( 'You can revoke sharing or delete your account at any time from AnchorKit -> Account.', 'anchorkit-table-of-contents' );
    return sprintf(
        '<strong>%1$s</strong><br>%2$s<br><br>%3$s<br><br>%4$s',
        $greeting,
        $privacy,
        $control,
        $terms_html
    );
}

/**
 * Redirect to AnchorKit settings after successful upgrade
 *
 * SECURITY REVIEW:
 * ---------------
 * ✓ Input Sanitization: $_GET['page'] sanitized with sanitize_key()
 * ✓ Safe Redirect: Using wp_safe_redirect() to prevent open redirects
 * ✓ Hardcoded URL: Redirect destination is hardcoded, not user-controlled
 * ✓ Nonce Verification: Not needed here - Freemius SDK verifies before calling this hook
 * ✓ Loop Prevention: Checks current page to avoid redirect loops
 * ✓ Early Exit: Uses exit after redirect per WordPress best practices
 *
 * @param string      $plan_change Can be 'none', 'upgraded', 'downgraded', 'changed', etc.
 * @param object|null $plan The new plan object
 * @return void
 */
function anchorkit_redirect_after_upgrade(  $plan_change, $plan  ) {
    // Only redirect if this was an upgrade or activation
    if ( $plan_change === 'upgraded' || $plan_change === 'activated' ) {
        // Check if we're already on the settings page to avoid redirect loops
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Freemius handles nonce verification
        $current_page = ( isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : '' );
        if ( $current_page !== 'anchorkit-settings' ) {
            // Redirect to settings page to show unlocked features
            wp_safe_redirect( admin_url( 'options-general.php?page=anchorkit-settings' ) );
            exit;
        }
    }
}

/**
 * Add custom styling for the AnchorKit Pro upgrade notice
 *
 * SECURITY & BEST PRACTICES REVIEW:
 * ---------------------------------
 * ✓ XSS Prevention: No user input in CSS - all values are hardcoded
 * ✓ Inline vs External: Inline CSS is CORRECT choice here because:
 *   - Small amount of CSS (~60 lines) specific to one notice
 *   - Reduces HTTP requests (performance best practice)
 *   - Only loads when needed (admin pages only)
 *   - WordPress VIP and major plugins use inline for small CSS
 * ✓ Script Escaping: JavaScript uses strict mode and proper jQuery wrapper
 * ✓ No SQL: Pure front-end styling, no database queries
 * ✓ Capability Check: is_admin() ensures only admin users see this
 * ✓ DOM Manipulation: jQuery sanitizes HTML manipulation automatically
 * ✓ Specificity: Uses data-id selectors to avoid style conflicts
 *
 * @return void
 */
function anchorkit_enqueue_upgrade_notice_styles() {
    // Only load on admin pages
    if ( !is_admin() ) {
        return;
    }
    // Inline CSS for upgrade notice - safe as it contains no user input
    // Using attribute selectors for specificity without relying on IDs
    $custom_css = "\n        /* Ensure the notice takes full width and moves above other header elements */\n        .fs-notice[data-id='plan_upgraded'].fs-notice.updated,\n        .fs-notice.fs-slug-anchorkit[data-id='plan_upgraded'] {\n            border-left: 4px solid #7C3AED !important;\n            background: linear-gradient(135deg, #F3F4F6 0%, #EDE9FE 100%) !important;\n            padding: 24px 32px !important;\n            box-shadow: 0 4px 12px rgba(124, 58, 237, 0.15) !important;\n            width: 100% !important; /* Full width for top-level placement */\n            max-width: 1000px !important;\n            margin: 0 0 30px 0 !important; /* Large gap below to separate from logo */\n            border-radius: 8px !important;\n            border: 1px solid rgba(124, 58, 237, 0.2) !important;\n            border-left-width: 5px !important;\n            box-sizing: border-box !important;\n            position: relative !important;\n            float: none !important;\n            display: block !important;\n        }\n        \n        /* Hide the plugin title badge */\n        .fs-notice[data-id='plan_upgraded'] .fs-plugin-title {\n            display: none !important;\n        }\n        \n        .fs-notice[data-id='plan_upgraded'] .fs-notice-body {\n            color: #1F2937 !important;\n            font-size: 15px !important;\n            line-height: 1.6 !important;\n            margin: 0 !important;\n        }\n        \n        .fs-notice[data-id='plan_upgraded'] h3,\n        .fs-notice[data-id='plan_upgraded'] strong {\n            color: #7C3AED !important;\n            font-weight: 600 !important;\n            font-size: 18px !important;\n            margin: 0 0 10px 0 !important;\n            display: block !important;\n            white-space: nowrap !important;\n        }\n        \n        .fs-notice[data-id='plan_upgraded'] .button-primary {\n            background: #7C3AED !important;\n            border-color: #6D28D9 !important;\n            text-shadow: none !important;\n            box-shadow: 0 2px 4px rgba(124, 58, 237, 0.2) !important;\n            margin-top: 15px !important;\n        }\n        \n        .fs-notice[data-id='plan_upgraded'] .button-primary:hover {\n            background: #6D28D9 !important;\n            border-color: #5B21B6 !important;\n        }\n        \n        /* Hide all lists and empty elements in upgrade notice */\n        .fs-notice[data-id='plan_upgraded'] ol,\n        .fs-notice[data-id='plan_upgraded'] ul {\n            display: none !important;\n        }\n        \n        .fs-notice[data-id='plan_upgraded'] ol li,\n        .fs-notice[data-id='plan_upgraded'] ul li {\n            display: none !important;\n        }\n    ";
    wp_add_inline_style( 'common', $custom_css );
    // Enqueue cleanup script
    // Note: Using jQuery (already loaded in WP admin) for DOM manipulation
    wp_enqueue_script( 'jquery' );
    wp_add_inline_script( 'jquery', '(function($) {
            "use strict";
            $(document).ready(function() {
                var upgradeNotice = $(".fs-notice[data-id=\\"plan_upgraded\\"]");
                var settingsContainer = $(".anchorkit-settings-wrap"); // Primary Settings Wrapper
                
                if (upgradeNotice.length) {
                    // Move the notice to the top of the AnchorKit settings container
                    if (settingsContainer.length) {
                        settingsContainer.prepend(upgradeNotice);
                    }

                    // Remove all ordered/unordered lists and their items
                    upgradeNotice.find("ol, ul").remove();
                    
                    // Clean up standalone formatting artifacts
                    upgradeNotice.find(".fs-notice-body").html(function(i, html) {
                        return html
                            .replace(/\\d+\\.\\s*\\.?/g, "")
                            .replace(/\\(\\)\\.?/g, "")
                            .trim();
                    });
                }
            });
        })(jQuery);', 'after' );
}

/**
 * Plugin activation hook
 * Set up default options and perform any necessary database operations
 */
function anchorkit_activate() {
    // Store activation timestamp
    if ( !get_option( 'anchorkit_activated_time' ) ) {
        add_option( 'anchorkit_activated_time', time() );
    }
    // Store plugin version for future migrations
    add_option( 'anchorkit_version', ANCHORKIT_PLUGIN_VERSION );
    // Set default options for TOC if they don't exist
    $default_options = array(
        'anchorkit_toc_enabled'              => true,
        'anchorkit_toc_automatic_insertion'  => true,
        'anchorkit_toc_position'             => 'before_first_heading',
        'anchorkit_toc_post_types'           => array('post', 'page'),
        'anchorkit_toc_min_headings'         => 2,
        'anchorkit_toc_include_headings'     => array('h2', 'h3', 'h4'),
        'anchorkit_toc_show_label'           => true,
        'anchorkit_toc_title_text'           => 'Table of Contents',
        'anchorkit_toc_style_preset'         => 'minimal',
        'anchorkit_toc_theme'                => 'system',
        'anchorkit_toc_hierarchical_view'    => true,
        'anchorkit_toc_collapsible'          => true,
        'anchorkit_toc_initial_state'        => 'expanded',
        'anchorkit_toc_smooth_scroll'        => true,
        'anchorkit_toc_scroll_offset'        => 0,
        'anchorkit_toc_bullet_style'         => 'disc',
        'anchorkit_toc_live_preview_enabled' => true,
    );
    foreach ( $default_options as $option_name => $default_value ) {
        if ( get_option( $option_name ) === false ) {
            add_option( $option_name, $default_value );
        }
    }
    // Flush rewrite rules in case we add any custom post types or endpoints in the future
    flush_rewrite_rules();
}

register_activation_hook( __FILE__, 'anchorkit_activate' );
/**
 * Plugin deactivation hook
 * Clean up temporary data (but keep settings for potential reactivation)
 */
function anchorkit_deactivate() {
    // Flush rewrite rules
    flush_rewrite_rules();
    // Clear any transients
    delete_transient( 'anchorkit_toc_cache' );
    // Note: We intentionally don't delete options here
    // Users expect their settings to persist if they reactivate
    // Options are only deleted via anchorkit_fs_uninstall_cleanup() when plugin is fully removed
}

register_deactivation_hook( __FILE__, 'anchorkit_deactivate' );
// Pro detection lives in includes/helpers.php so it can be shared across AJAX, blocks and widgets.