<?php

if (!defined('ABSPATH')) {
    exit;
}

/**
 * SEO Fury Revisions
 *
 * Tracks changes to SEO metadata over time, allowing users to see
 * what changed and when, and to restore previous values.
 */
class SEO_Fury_Revisions
{

    /**
     * Maximum number of revisions to keep per post.
     */
    const MAX_REVISIONS = 20;

    /**
     * Post meta key for storing revision history.
     */
    const META_KEY = '_seo_fury_revisions';

    /**
     * Meta keys to track (new prefix).
     */
    private $tracked_keys = array(
        'title'              => '_seo_fury_title',
        'description'        => '_seo_fury_description',
        'focus_keyword'      => '_seo_fury_focus_keyword',
        'robots_noindex'     => '_seo_fury_page_robots_noindex',
        'robots_nofollow'    => '_seo_fury_page_robots_nofollow',
        'canonical_url'      => '_seo_fury_canonical',
    );

    /**
     * Legacy meta keys to also track (old prefix).
     */
    private $legacy_keys = array(
        'title'              => '_my_seo_title',
        'description'        => '_my_seo_description',
        'focus_keyword'      => '_my_seo_focus_keyword',
        'robots_noindex'     => '_my_seo_robots_noindex',
        'robots_nofollow'    => '_my_seo_robots_nofollow',
        'canonical_url'      => '_my_seo_canonical',
    );

    /**
     * Human-readable labels for tracked fields.
     */
    private $field_labels = array();

    /**
     * Snapshot of meta values captured before save.
     *
     * @var array|null
     */
    private $pre_save_snapshot = null;

    public function __construct()
    {
        $this->field_labels = array(
            'title'           => __('SEO Title', 'seo-fury'),
            'description'     => __('Meta Description', 'seo-fury'),
            'focus_keyword'   => __('Focus Keyword', 'seo-fury'),
            'robots_noindex'  => __('NoIndex', 'seo-fury'),
            'robots_nofollow' => __('NoFollow', 'seo-fury'),
            'canonical_url'   => __('Canonical URL', 'seo-fury'),
        );

        $this->init_hooks();
    }

    /**
     * Register WordPress hooks.
     */
    private function init_hooks()
    {
        // Capture the current meta before save_post fires
        add_action('pre_post_update', array($this, 'capture_pre_save_snapshot'), 10, 1);

        // Record revision after SEO meta is saved (priority 20, after post-meta save at 10)
        add_action('save_post', array($this, 'record_revision'), 20, 2);

        // AJAX handler for restoring revisions
        add_action('wp_ajax_seo_fury_restore_revision', array($this, 'ajax_restore_revision'));
    }

    /**
     * Capture a snapshot of the current SEO meta values before the post is updated.
     *
     * @param int $post_id Post ID.
     */
    public function capture_pre_save_snapshot($post_id)
    {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }

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

        $this->pre_save_snapshot = $this->get_current_values($post_id);
    }

    /**
     * Get the current SEO meta values for a post.
     *
     * @param int $post_id Post ID.
     * @return array Associative array of field_name => value.
     */
    private function get_current_values($post_id)
    {
        $values = array();

        foreach ($this->tracked_keys as $field => $meta_key) {
            $value = get_post_meta($post_id, $meta_key, true);

            // If the new-prefix key is empty, check the legacy key
            if ($value === '' || $value === false) {
                $legacy_key = isset($this->legacy_keys[$field]) ? $this->legacy_keys[$field] : '';
                if ($legacy_key) {
                    $legacy_value = get_post_meta($post_id, $legacy_key, true);
                    if ($legacy_value !== '' && $legacy_value !== false) {
                        $value = $legacy_value;
                    }
                }
            }

            $values[$field] = is_string($value) ? $value : strval($value);
        }

        return $values;
    }

    /**
     * Record a revision after SEO meta has been saved.
     *
     * Compares the pre-save snapshot to the current (just-saved) values
     * and stores any differences.
     *
     * @param int     $post_id Post ID.
     * @param WP_Post $post    Post object.
     */
    public function record_revision($post_id, $post)
    {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }

        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }

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

        // We need the pre-save snapshot
        if ($this->pre_save_snapshot === null) {
            return;
        }

        $old_values = $this->pre_save_snapshot;
        $new_values = $this->get_current_values($post_id);

        // Reset snapshot
        $this->pre_save_snapshot = null;

        // Find changes
        $changes = array();
        foreach ($old_values as $field => $old_val) {
            $new_val = isset($new_values[$field]) ? $new_values[$field] : '';
            if ($old_val !== $new_val) {
                $changes[$field] = array(
                    'old' => $old_val,
                    'new' => $new_val,
                );
            }
        }

        // Nothing changed — no revision needed
        if (empty($changes)) {
            return;
        }

        $this->add_revision_entry($post_id, $changes);
    }

    /**
     * Add a revision entry to the post's revision history.
     *
     * @param int    $post_id Post ID.
     * @param array  $changes Array of field changes.
     * @param string $type    Optional. Type of revision: 'edit' or 'restore'.
     */
    private function add_revision_entry($post_id, $changes, $type = 'edit')
    {
        $revisions = get_post_meta($post_id, self::META_KEY, true);
        if (!is_array($revisions)) {
            $revisions = array();
        }

        $entry = array(
            'date'    => current_time('mysql'),
            'user_id' => get_current_user_id(),
            'changes' => $changes,
            'type'    => $type,
        );

        // Prepend new revision (newest first)
        array_unshift($revisions, $entry);

        // Trim to max revisions
        if (count($revisions) > self::MAX_REVISIONS) {
            $revisions = array_slice($revisions, 0, self::MAX_REVISIONS);
        }

        update_post_meta($post_id, self::META_KEY, $revisions);
    }

    /**
     * Render the SEO revisions panel for the post editor metabox.
     *
     * This method is intended to be called from class-post-meta.php
     * inside the metabox rendering.
     *
     * @param WP_Post $post The current post object.
     */
    public function render_revisions_panel($post)
    {
        $revisions = get_post_meta($post->ID, self::META_KEY, true);
        if (!is_array($revisions)) {
            $revisions = array();
        }

        $nonce = wp_create_nonce('seo_fury_revisions_nonce');
        ?>
        <div class="seo-fury-revisions-panel" style="margin-top: 16px;">
            <div
                class="seo-fury-revisions-header"
                style="display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding: 10px 14px; background: var(--sf-gray-50, #f8fafc); border: 1px solid var(--sf-gray-200, #e2e8f0); border-radius: 6px; user-select: none;"
                onclick="jQuery(this).next('.seo-fury-revisions-body').slideToggle(200); jQuery(this).find('.dashicons').toggleClass('dashicons-arrow-down-alt2 dashicons-arrow-up-alt2');"
            >
                <span style="display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--sf-gray-900, #1e293b);">
                    <span class="dashicons dashicons-backup" style="font-size: 16px; width: 16px; height: 16px; color: #6B46C1;"></span>
                    <?php esc_html_e('SEO Revision History', 'seo-fury'); ?>
                    <?php if (!empty($revisions)) : ?>
                        <span style="background: #E2E8F0; color: var(--sf-gray-700, #475569); font-size: 11px; padding: 1px 7px; border-radius: 10px; font-weight: 500;">
                            <?php echo intval( count($revisions) ); ?>
                        </span>
                    <?php endif; ?>
                </span>
                <span class="dashicons dashicons-arrow-down-alt2" style="font-size: 14px; width: 14px; height: 14px; color: var(--sf-gray-400, #94a3b8);"></span>
            </div>

            <div class="seo-fury-revisions-body" style="display: none; border: 1px solid var(--sf-gray-200, #e2e8f0); border-top: none; border-radius: 0 0 6px 6px; max-height: 400px; overflow-y: auto;">
                <?php if (empty($revisions)) : ?>
                    <div style="padding: 20px; text-align: center; color: var(--sf-gray-400, #94a3b8); font-size: 13px;">
                        <?php esc_html_e('No SEO revisions recorded yet. Changes will appear here after you update SEO fields.', 'seo-fury'); ?>
                    </div>
                <?php else : ?>
                    <?php foreach ($revisions as $index => $revision) : ?>
                        <?php
                        $user = get_userdata($revision['user_id']);
                        $display_name = $user ? $user->display_name : __('Unknown User', 'seo-fury');
                        $is_restore = isset($revision['type']) && $revision['type'] === 'restore';
                        $date_formatted = mysql2date(get_option('date_format') . ' ' . get_option('time_format'), $revision['date']);
                        ?>
                        <div class="seo-fury-revision-item" style="padding: 12px 14px; border-bottom: 1px solid var(--sf-gray-200, #f1f5f9); <?php echo esc_attr( $index === 0 ? 'background: #FEFCE8;' : '' ); ?>">
                            <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
                                <div style="font-size: 12px; color: var(--sf-gray-500, #64748b);">
                                    <strong style="color: var(--sf-gray-700, #334155);"><?php echo esc_html($display_name); ?></strong>
                                    &mdash;
                                    <?php echo esc_html($date_formatted); ?>
                                    <?php if ($is_restore) : ?>
                                        <span style="background: #DBEAFE; color: #1E40AF; font-size: 10px; padding: 1px 6px; border-radius: 8px; margin-left: 4px;">
                                            <?php esc_html_e('Restored', 'seo-fury'); ?>
                                        </span>
                                    <?php endif; ?>
                                </div>
                                <button
                                    type="button"
                                    class="button button-small seo-fury-restore-revision"
                                    data-post-id="<?php echo esc_attr($post->ID); ?>"
                                    data-index="<?php echo esc_attr($index); ?>"
                                    data-nonce="<?php echo esc_attr($nonce); ?>"
                                    style="font-size: 11px; line-height: 1.8; min-height: 24px; padding: 0 8px;"
                                    title="<?php esc_attr_e('Restore the old values from this revision', 'seo-fury'); ?>"
                                >
                                    <span class="dashicons dashicons-undo" style="font-size: 13px; width: 13px; height: 13px; margin-top: 2px;"></span>
                                    <?php esc_html_e('Restore', 'seo-fury'); ?>
                                </button>
                            </div>
                            <div style="font-size: 12px; line-height: 1.6;">
                                <?php foreach ($revision['changes'] as $field => $change) : ?>
                                    <?php
                                    $label = isset($this->field_labels[$field]) ? $this->field_labels[$field] : $field;
                                    $old_display = $change['old'] !== '' ? $change['old'] : __('(empty)', 'seo-fury');
                                    $new_display = $change['new'] !== '' ? $change['new'] : __('(empty)', 'seo-fury');
                                    ?>
                                    <div style="margin-bottom: 4px; padding: 4px 8px; background: var(--sf-gray-50, #f8fafc); border-radius: 4px;">
                                        <strong style="color: var(--sf-gray-700, #475569);"><?php echo esc_html($label); ?>:</strong>
                                        <span style="color: #DC2626; text-decoration: line-through;"><?php echo esc_html($old_display); ?></span>
                                        <span style="color: var(--sf-gray-400, #94a3b8); margin: 0 2px;">&rarr;</span>
                                        <span style="color: #16A34A;"><?php echo esc_html($new_display); ?></span>
                                    </div>
                                <?php endforeach; ?>
                            </div>
                        </div>
                    <?php endforeach; ?>
                <?php endif; ?>
            </div>
        </div>

        <script type="text/javascript">
            jQuery(document).ready(function($) {
                $('.seo-fury-restore-revision').on('click', function(e) {
                    e.preventDefault();

                    var $btn = $(this);
                    var confirmMsg = <?php echo wp_json_encode(__('Are you sure you want to restore this revision? The current SEO values will be overwritten with the old values from this revision.', 'seo-fury')); ?>;

                    if (!confirm(confirmMsg)) {
                        return;
                    }

                    var postId = $btn.data('post-id');
                    var index = $btn.data('index');
                    var nonce = $btn.data('nonce');
                    var restoringText = <?php echo wp_json_encode(__('Restoring...', 'seo-fury')); ?>;
                    var restoreText = <?php echo wp_json_encode(__('Restore', 'seo-fury')); ?>;
                    var failedText = <?php echo wp_json_encode(__('Failed to restore revision.', 'seo-fury')); ?>;
                    var errorText = <?php echo wp_json_encode(__('An error occurred. Please try again.', 'seo-fury')); ?>;

                    $btn.prop('disabled', true).text(restoringText);

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'seo_fury_restore_revision',
                            post_id: postId,
                            revision_index: index,
                            _ajax_nonce: nonce
                        },
                        success: function(response) {
                            if (response.success) {
                                window.location.reload();
                            } else {
                                alert(response.data || failedText);
                                $btn.prop('disabled', false).html(
                                    '<span class="dashicons dashicons-undo" style="font-size:13px;width:13px;height:13px;margin-top:2px;"></span> ' + restoreText
                                );
                            }
                        },
                        error: function() {
                            alert(errorText);
                            $btn.prop('disabled', false).html(
                                '<span class="dashicons dashicons-undo" style="font-size:13px;width:13px;height:13px;margin-top:2px;"></span> ' + restoreText
                            );
                        }
                    });
                });
            });
        </script>
        <?php
    }

    /**
     * AJAX handler to restore a revision.
     *
     * Restores the "old" values from the specified revision back onto the post meta,
     * and records a new revision entry noting the restore.
     */
    public function ajax_restore_revision()
    {
        check_ajax_referer('seo_fury_revisions_nonce');

        $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
        $revision_index = isset($_POST['revision_index']) ? absint($_POST['revision_index']) : 0;

        if (!$post_id) {
            wp_send_json_error(__('Invalid post ID.', 'seo-fury'));
        }

        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error(__('You do not have permission to edit this post.', 'seo-fury'));
        }

        $revisions = get_post_meta($post_id, self::META_KEY, true);
        if (!is_array($revisions) || !isset($revisions[$revision_index])) {
            wp_send_json_error(__('Revision not found.', 'seo-fury'));
        }

        $target_revision = $revisions[$revision_index];
        $changes = $target_revision['changes'];

        if (empty($changes)) {
            wp_send_json_error(__('No changes to restore.', 'seo-fury'));
        }

        // Capture current values before restoring (for the new revision entry)
        $current_values = $this->get_current_values($post_id);

        // Build the restore changes record (tracks what the restore itself changed)
        $restore_changes = array();

        foreach ($changes as $field => $change) {
            $old_value = $change['old'];
            $meta_key = isset($this->tracked_keys[$field]) ? $this->tracked_keys[$field] : '';

            if (!$meta_key) {
                continue;
            }

            $current_val = isset($current_values[$field]) ? $current_values[$field] : '';

            // Restore to the old value from that revision
            update_post_meta($post_id, $meta_key, $old_value);

            // Also update legacy key if it exists and had a value
            if (isset($this->legacy_keys[$field])) {
                $legacy_key = $this->legacy_keys[$field];
                $legacy_val = get_post_meta($post_id, $legacy_key, true);
                if ($legacy_val !== '' && $legacy_val !== false) {
                    update_post_meta($post_id, $legacy_key, $old_value);
                }
            }

            // Record the change from the restore
            if ($current_val !== $old_value) {
                $restore_changes[$field] = array(
                    'old' => $current_val,
                    'new' => $old_value,
                );
            }
        }

        // Add a new revision entry for the restore action
        if (!empty($restore_changes)) {
            $this->add_revision_entry($post_id, $restore_changes, 'restore');
        }

        wp_send_json_success(array(
            'message' => __('Revision restored successfully.', 'seo-fury'),
        ));
    }

    /**
     * Get revisions for a post.
     *
     * @param int $post_id Post ID.
     * @return array Array of revision entries.
     */
    public function get_revisions($post_id)
    {
        $revisions = get_post_meta($post_id, self::META_KEY, true);
        return is_array($revisions) ? $revisions : array();
    }

    /**
     * Clear all revisions for a post.
     *
     * @param int $post_id Post ID.
     * @return bool True on success, false on failure.
     */
    public function clear_revisions($post_id)
    {
        return delete_post_meta($post_id, self::META_KEY);
    }
}
