<?php
/**
 * Name: Content Expiration
 * Description: Automatically unpublish posts after specified expiration date and time for time-sensitive content like promotions, events, or seasonal offers requiring automatic removal
 * Category: Content
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Limited-Time Promotions",
 *     "description": "Automatically unpublish Black Friday sale pages or seasonal promotion content after campaign ends without manual monitoring."
 *   },
 *   {
 *     "title": "Event Listings",
 *     "description": "Set webinar or conference pages to auto-expire after event date passes, keeping site content current without manual cleanup."
 *   },
 *   {
 *     "title": "Job Postings",
 *     "description": "Automatically unpublish job listings after 30 days to ensure only current opportunities appear on careers page."
 *   }
 * ]
 * Added: 2025-07-20
 * Tags: expiration, schedule, unpublish, automation, posts
 * WhereToFind: {
 *   "location": "Post Editor Sidebar",
 *   "url": "post.php",
 *   "instructions": "Set expiration date in post editor - posts will auto-unpublish after date",
 *   "newTab": false
 * }
 */

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

class SwitchboardContentExpiration {

    public function __construct() {
        add_action('add_meta_boxes', [$this, 'add_meta_box']);
        add_action('save_post', [$this, 'save_meta_box'], 10, 2);
        add_action('switchboard_check_expiration', [$this, 'check_expired_posts']);

        // Fix any existing posts with wrong date format on module load
        add_action('init', [$this, 'fix_existing_date_formats'], 11);

        // Check immediately on page load (for instant expiration)
        add_action('wp', [$this, 'check_single_post_expiration']);

        // Run expiration check on admin_init for more frequent checks
        add_action('admin_init', [$this, 'check_expired_posts']);
    }

    /**
     * Called when module is activated
     * Schedules cron events
     */
    public function activate() {
        if (!wp_next_scheduled('switchboard_check_expiration')) {
            wp_schedule_event(time(), 'hourly', 'switchboard_check_expiration');
        }
    }

    /**
     * Called when module is deactivated
     * Clears all scheduled cron events
     */
    public function deactivate() {
        wp_clear_scheduled_hook('switchboard_check_expiration');
        delete_transient('switchboard_expiration_cron_checked');
    }

    /**
     * Fix existing posts that have HTML5 datetime-local format
     */
    public function fix_existing_date_formats() {
        // Only run once per day to avoid performance issues
        $last_fix = get_option('switchboard_expiration_last_format_fix', 0);
        if (time() - $last_fix < DAY_IN_SECONDS) {
            return;
        }

        global $wpdb;

        // Find posts with T format (HTML5 datetime-local)
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time format migration query
        $posts_to_fix = $wpdb->get_results("
            SELECT post_id, meta_value
            FROM {$wpdb->postmeta}
            WHERE meta_key = '_switchboard_expiration_date'
            AND meta_value LIKE '%T%'
            AND meta_value != ''
        ");

        if (!empty($posts_to_fix)) {
            foreach ($posts_to_fix as $post) {
                // Convert: 2025-10-25T12:18 -> 2025-10-25 12:18:00
                $fixed_date = str_replace('T', ' ', $post->meta_value);
                // Add seconds if missing
                if (strlen($fixed_date) === 16) {
                    $fixed_date .= ':00';
                }

                update_post_meta($post->post_id, '_switchboard_expiration_date', $fixed_date);
            }

        }

        update_option('switchboard_expiration_last_format_fix', time());
    }

    /**
     * Add meta box to post editor
     */
    public function add_meta_box() {
        $modules = get_option('switchboard_modules', []);
        $settings = isset($modules['content-expiration']['settings']) ? $modules['content-expiration']['settings'] : [];
        $post_types = ['post', 'page']; // Default

        if (isset($settings['post_types']) && is_string($settings['post_types']) && !empty($settings['post_types'])) {
            $post_types = array_map('trim', explode(',', $settings['post_types']));
        }

        foreach ($post_types as $post_type) {
            add_meta_box(
                'switchboard_expiration',
                __('Content Expiration', 'wpswitchboard') . ' <span class="switchboard-badge">SWITCHBOARD</span>',
                [$this, 'render_meta_box'],
                trim($post_type),
                'side',
                'high'
            );
        }
    }

    /**
     * Render the meta box
     */
    public function render_meta_box($post) {
        wp_nonce_field('switchboard_expiration', 'switchboard_expiration_nonce');
        $expiration_date = get_post_meta($post->ID, '_switchboard_expiration_date', true);

        // Convert MySQL format to HTML5 datetime-local format for display
        $display_value = '';
        if ($expiration_date) {
            $timestamp = strtotime($expiration_date);
            if ($timestamp) {
                $display_value = gmdate('Y-m-d\TH:i', $timestamp);
            }
        }

        // Get WordPress timezone for display
        $timezone_string = wp_timezone_string();
        $current_time = current_time('Y-m-d H:i:s');
        ?>
        <p>
            <label for="switchboard_expiration_date"><?php esc_html_e('Expiration Date:', 'wpswitchboard'); ?></label>
            <input type="datetime-local"
                   id="switchboard_expiration_date"
                   name="switchboard_expiration_date"
                   value="<?php echo esc_attr($display_value); ?>"
                   class="sb-sp-w-full">
        </p>
        <p class="sb-sp-description">
            <?php
            printf(
                /* translators: 1: timezone, 2: current time */
                esc_html__('Timezone: %1$s (Current: %2$s)', 'wpswitchboard'),
                '<strong>' . esc_html($timezone_string) . '</strong>',
                '<strong>' . esc_html($current_time) . '</strong>'
            );
            ?>
        </p>
        <p class="sb-sp-description"><?php esc_html_e('Post will be unpublished when this date/time is reached.', 'wpswitchboard'); ?></p>
        <?php
    }

    /**
     * Save meta box data
     */
    public function save_meta_box($post_id, $post) {
        // Security checks
        if (!isset($_POST['switchboard_expiration_nonce']) ||
            !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['switchboard_expiration_nonce'])), 'switchboard_expiration')) {
            return;
        }

        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }

        if (!current_user_can('edit_post', $post_id)) {
            return;
        }

        // Save or delete expiration date
        if (isset($_POST['switchboard_expiration_date']) && !empty($_POST['switchboard_expiration_date'])) {
            $expiration_date = sanitize_text_field(wp_unslash($_POST['switchboard_expiration_date']));

            // Convert HTML5 datetime-local format to MySQL format
            // From: 2025-10-25T12:18
            // To:   2025-10-25 12:18:00
            $expiration_date = str_replace('T', ' ', $expiration_date);

            // Add seconds if not present
            if (strlen($expiration_date) === 16) {
                $expiration_date .= ':00';
            }

            update_post_meta($post_id, '_switchboard_expiration_date', $expiration_date);

            // Check immediately if this post should be expired right now
            $this->check_single_post_immediately($post_id, $expiration_date);
        } else {
            delete_post_meta($post_id, '_switchboard_expiration_date');
        }
    }

    /**
     * Check a single post immediately after saving (for instant expiration)
     */
    private function check_single_post_immediately($post_id, $expiration_date) {
        $current_datetime = current_time('Y-m-d H:i:s');

        // If expiration date is in the past and post is published
        if ($expiration_date <= $current_datetime) {
            $post = get_post($post_id);
            if ($post && $post->post_status === 'publish') {
                wp_update_post([
                    'ID' => $post_id,
                    'post_status' => 'draft'
                ], true);

            }
        }
    }

    /**
     * Check if current post being viewed is expired (instant expiration on page load)
     */
    public function check_single_post_expiration() {
        if (!is_singular()) {
            return;
        }

        global $post;
        if (!$post || $post->post_status !== 'publish') {
            return;
        }

        $expiration_date = get_post_meta($post->ID, '_switchboard_expiration_date', true);
        if (empty($expiration_date)) {
            return;
        }

        $current_datetime = current_time('Y-m-d H:i:s');

        // Handle both date formats
        $check_date = str_replace('T', ' ', $expiration_date);
        if (strlen($check_date) === 16) {
            $check_date .= ':00';
        }

        // If expired, unpublish immediately and redirect
        if ($check_date <= $current_datetime) {
            wp_update_post([
                'ID' => $post->ID,
                'post_status' => 'draft'
            ], true);


            // Redirect to homepage or 404
            wp_safe_redirect(home_url());
            exit;
        }
    }

    /**
     * Check for expired posts and unpublish them
     */
    public function check_expired_posts() {
        global $wpdb;

        if (!$wpdb) {
            return;
        }

        // Get current time in WordPress timezone
        $current_datetime = current_time('Y-m-d H:i:s');

        // Find all expired posts
        // Note: We need to handle both formats during transition:
        // - New format: 2025-10-25 12:18:00 (MySQL)
        // - Old format: 2025-10-25T12:18 (HTML5 datetime-local)
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Complex meta+post join query for expiration processing
        $expired_posts = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT pm.post_id, pm.meta_value, p.post_status
                FROM {$wpdb->postmeta} pm
                INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
                WHERE pm.meta_key = '_switchboard_expiration_date'
                AND pm.meta_value != ''
                AND (
                    pm.meta_value <= %s
                    OR REPLACE(pm.meta_value, 'T', ' ') <= %s
                )
                AND p.post_status = 'publish'",
                $current_datetime,
                $current_datetime
            )
        );

        if (!empty($expired_posts)) {
            foreach ($expired_posts as $post) {
                // Unpublish the post
                $result = wp_update_post([
                    'ID' => $post->post_id,
                    'post_status' => 'draft'
                ], true);

                if (is_wp_error($result)) {
                } else {
                }
            }
        }
    }

    public function render_settings($settings = []) {
        $selected_post_types = isset($settings['post_types']) ? explode(',', $settings['post_types']) : ['post', 'page'];
        $selected_post_types = array_map('trim', $selected_post_types);
        
        // Exclude media and system post types
        $all_post_types = get_post_types(['public' => true], 'objects');
        $exclude_types = ['attachment', 'wp_block', 'wp_template', 'wp_template_part', 'wp_navigation'];
        $all_post_types = array_diff_key($all_post_types, array_flip($exclude_types));
        ?>
        <input type="hidden" name="module" value="content-expiration">
        <input type="hidden" name="setting_post_types" id="setting_post_types" value="<?php echo esc_attr(implode(',', $selected_post_types)); ?>">

        <div class="sb-sp-group">
            <label class="sb-sp-label"><?php esc_html_e('Select Post Types', 'wpswitchboard'); ?></label>
            <div class="switchboard-tag-selector" data-input-id="setting_post_types">
                <div class="switchboard-selected-tags" id="selected-post-types">
                <?php foreach ($selected_post_types as $pt_slug):
                    if (isset($all_post_types[$pt_slug])): ?>
                        <span class="switchboard-tag" data-value="<?php echo esc_attr($pt_slug); ?>">
                            <?php echo esc_html($all_post_types[$pt_slug]->label); ?>
                            <span class="remove-tag">×</span>
                        </span>
                    <?php endif;
                endforeach; ?>
            </div>
            <div class="switchboard-available-tags">
                <?php foreach ($all_post_types as $pt_slug => $pt_obj):
                    $is_selected = in_array($pt_slug, $selected_post_types) ? 'selected' : ''; ?>
                    <span class="switchboard-tag-option <?php echo esc_attr($is_selected); ?>" data-value="<?php echo esc_attr($pt_slug); ?>">
                        <?php echo esc_html($pt_obj->label); ?>
                    </span>
                <?php endforeach; ?>
            </div>
        </div>
        <p class="sb-sp-description"><?php esc_html_e('Enable content expiration for these post types.', 'wpswitchboard'); ?></p>
        </div>
        <?php
    }

    /**
     * Register data locations for cleanup system
     *
     * @return array Data storage locations
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_content-expiration_settings',
                'switchboard_expiration_last_format_fix'
            ],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [
                '_switchboard_expiration_date',
            ],
            'user_meta' => [],
        ];
    }
}

$switchboard_instance = new SwitchboardContentExpiration();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);

// Ensure cron is scheduled (runs once, uses transient to avoid repeated checks)
// This handles cases where module was active before activate() method existed
if (!get_transient('switchboard_expiration_cron_checked')) {
    if (!wp_next_scheduled('switchboard_check_expiration')) {
        wp_schedule_event(time(), 'hourly', 'switchboard_check_expiration');
    }
    // Set transient for 24 hours - only check once per day
    set_transient('switchboard_expiration_cron_checked', 1, DAY_IN_SECONDS);
}
