<?php
/**
 * Comment Image Boost moderation and cleanup tools.
 *
 * @package CommentImageBoost
 */

if (!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

class CIBOOST_Moderation_Tools {

    public function __construct() {
        add_action('admin_menu', array($this, 'add_submenu'), 20);
        add_action('admin_post_ciboost_moderate_comment_images', array($this, 'handle_bulk_action'));
        add_action('admin_post_ciboost_delete_orphans', array($this, 'handle_delete_orphans'));
    }

    public function add_submenu() {
        add_submenu_page(
            'comment-image-boost',
            __('Image Moderation', 'comment-image-boost'),
            __('Moderation', 'comment-image-boost'),
            'moderate_comments',
            'comment-image-boost-moderation',
            array($this, 'render_page')
        );
    }

    private function get_image_rows($status_filter = 'all', $post_id = 0) {
        $comments = get_comments(array(
            'status' => 'all',
            'number' => 200,
            'orderby' => 'comment_date_gmt',
            'order' => 'DESC',
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required to list comments with image metadata.
            'meta_key' => CIBOOST_Comment_Images::META_KEY,
            'post_id' => $post_id > 0 ? $post_id : 0,
        ));

        $rows = array();
        $upload_url = ciboost_get_comment_images_upload_url();
        $upload_dir = ciboost_get_comment_images_upload_dir();

        foreach ($comments as $comment) {
            $image_data = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::META_KEY, true);
            $filenames = is_array($image_data) ? $image_data : array($image_data);
            $statuses = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::STATUS_META_KEY, true);
            $captions = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::CAPTIONS_META_KEY, true);
            $attachments = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::ATTACHMENTS_META_KEY, true);

            $statuses = is_array($statuses) ? $statuses : array();
            $captions = is_array($captions) ? $captions : array();
            $attachments = is_array($attachments) ? $attachments : array();

            foreach ($filenames as $filename) {
                $filename = sanitize_file_name($filename);
                if ($filename === '') {
                    continue;
                }

                $status = isset($statuses[$filename]) ? sanitize_key($statuses[$filename]) : 'approved';
                if (!in_array($status, array('approved', 'pending', 'rejected'), true)) {
                    $status = 'approved';
                }
                if ($status_filter !== 'all' && $status !== $status_filter) {
                    continue;
                }

                $file_path = $upload_dir . $filename;
                $image_url = $upload_url . $filename;
                $thumb_url = $image_url;
                $attachment_id = isset($attachments[$filename]) ? absint($attachments[$filename]) : 0;

                if ($attachment_id > 0) {
                    $full_src = wp_get_attachment_image_src($attachment_id, 'full');
                    $thumb_src = wp_get_attachment_image_src($attachment_id, 'thumbnail');
                    if (!empty($full_src[0])) {
                        $image_url = $full_src[0];
                    }
                    if (!empty($thumb_src[0])) {
                        $thumb_url = $thumb_src[0];
                    }
                }

                $rows[] = array(
                    'comment' => $comment,
                    'filename' => $filename,
                    'status' => $status,
                    'caption' => isset($captions[$filename]) ? sanitize_text_field($captions[$filename]) : '',
                    'image_url' => $image_url,
                    'thumb_url' => $thumb_url,
                    'exists' => file_exists($file_path),
                );
            }
        }

        return $rows;
    }

    public function render_page() {
        if (!current_user_can('moderate_comments')) {
            return;
        }

        $filter_nonce_valid = isset($_GET['ciboost_filter_nonce']) &&
            wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['ciboost_filter_nonce'])), 'ciboost_filter_moderation');

        $status = $filter_nonce_valid && isset($_GET['ciboost_status']) ? sanitize_key(wp_unslash($_GET['ciboost_status'])) : 'all';
        if (!in_array($status, array('all', 'approved', 'pending', 'rejected'), true)) {
            $status = 'all';
        }

        $post_id = $filter_nonce_valid && isset($_GET['ciboost_post_id']) ? absint(wp_unslash($_GET['ciboost_post_id'])) : 0;
        $rows = $this->get_image_rows($status, $post_id);
        $orphans = $this->scan_orphan_files();
        $updated_notice_valid = isset($_GET['ciboost_updated'], $_GET['ciboost_notice_nonce']) &&
            '1' === sanitize_text_field(wp_unslash($_GET['ciboost_updated'])) &&
            wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['ciboost_notice_nonce'])), 'ciboost_moderation_notice');

        ?>
        <div class="wrap">
            <h1><?php esc_html_e('Comment Image Moderation', 'comment-image-boost'); ?></h1>

            <?php if ($updated_notice_valid): ?>
                <div class="notice notice-success is-dismissible"><p><?php esc_html_e('Images updated successfully.', 'comment-image-boost'); ?></p></div>
            <?php endif; ?>

            <form method="get" style="margin: 18px 0; display: flex; gap: 10px; align-items: end; flex-wrap: wrap;">
                <input type="hidden" name="page" value="comment-image-boost-moderation">
                <?php wp_nonce_field('ciboost_filter_moderation', 'ciboost_filter_nonce'); ?>
                <label>
                    <?php esc_html_e('Status', 'comment-image-boost'); ?><br>
                    <select name="ciboost_status">
                        <option value="all" <?php selected($status, 'all'); ?>><?php esc_html_e('All', 'comment-image-boost'); ?></option>
                        <option value="approved" <?php selected($status, 'approved'); ?>><?php esc_html_e('Approved', 'comment-image-boost'); ?></option>
                        <option value="pending" <?php selected($status, 'pending'); ?>><?php esc_html_e('Pending', 'comment-image-boost'); ?></option>
                        <option value="rejected" <?php selected($status, 'rejected'); ?>><?php esc_html_e('Rejected', 'comment-image-boost'); ?></option>
                    </select>
                </label>
                <label>
                    <?php esc_html_e('Post ID', 'comment-image-boost'); ?><br>
                    <input type="number" name="ciboost_post_id" value="<?php echo esc_attr($post_id); ?>" min="0" class="small-text">
                </label>
                <?php submit_button(__('Filter', 'comment-image-boost'), 'secondary', '', false); ?>
            </form>

            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="ciboost_moderate_comment_images">
                <?php wp_nonce_field('ciboost_moderate_comment_images'); ?>

                <div style="margin: 12px 0; display: flex; gap: 10px; align-items: center;">
                    <select name="ciboost_bulk_action">
                        <option value=""><?php esc_html_e('Bulk action', 'comment-image-boost'); ?></option>
                        <option value="approved"><?php esc_html_e('Approve', 'comment-image-boost'); ?></option>
                        <option value="pending"><?php esc_html_e('Mark pending', 'comment-image-boost'); ?></option>
                        <option value="rejected"><?php esc_html_e('Reject', 'comment-image-boost'); ?></option>
                        <option value="delete"><?php esc_html_e('Delete permanently', 'comment-image-boost'); ?></option>
                    </select>
                    <?php submit_button(__('Apply', 'comment-image-boost'), 'primary', '', false); ?>
                </div>

                <table class="widefat striped">
                    <thead>
                        <tr>
                            <td class="manage-column column-cb check-column"><input type="checkbox" onclick="document.querySelectorAll('.ciboost-image-select').forEach(function(el){el.checked = this.checked;}, this);"></td>
                            <th><?php esc_html_e('Image', 'comment-image-boost'); ?></th>
                            <th><?php esc_html_e('Status', 'comment-image-boost'); ?></th>
                            <th><?php esc_html_e('Comment', 'comment-image-boost'); ?></th>
                            <th><?php esc_html_e('Post', 'comment-image-boost'); ?></th>
                            <th><?php esc_html_e('File', 'comment-image-boost'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php if (empty($rows)): ?>
                            <tr><td colspan="6"><?php esc_html_e('No comment images found.', 'comment-image-boost'); ?></td></tr>
                        <?php endif; ?>
                        <?php foreach ($rows as $row): ?>
                            <?php $value = absint($row['comment']->comment_ID) . '|' . $row['filename']; ?>
                            <tr>
                                <th scope="row" class="check-column"><input class="ciboost-image-select" type="checkbox" name="ciboost_images[]" value="<?php echo esc_attr($value); ?>"></th>
                                <td><a href="<?php echo esc_url($row['image_url']); ?>" target="_blank"><img src="<?php echo esc_url($row['thumb_url']); ?>" alt="" style="width: 74px; height: 74px; object-fit: cover; border-radius: 6px; border: 1px solid #dcdcde;"></a></td>
                                <td>
                                    <strong><?php echo esc_html(ucfirst($row['status'])); ?></strong>
                                    <?php if (!$row['exists']): ?>
                                        <br><span style="color:#b32d2e;"><?php esc_html_e('File missing', 'comment-image-boost'); ?></span>
                                    <?php endif; ?>
                                </td>
                                <td>
                                    <a href="<?php echo esc_url(get_edit_comment_link($row['comment']->comment_ID)); ?>">#<?php echo esc_html($row['comment']->comment_ID); ?></a><br>
                                    <?php echo esc_html(wp_trim_words($row['comment']->comment_content, 16)); ?>
                                </td>
                                <td>
                                    <?php if ($row['comment']->comment_post_ID): ?>
                                        <a href="<?php echo esc_url(get_edit_post_link($row['comment']->comment_post_ID)); ?>"><?php echo esc_html(get_the_title($row['comment']->comment_post_ID)); ?></a>
                                    <?php else: ?>
                                        -
                                    <?php endif; ?>
                                </td>
                                <td><code><?php echo esc_html($row['filename']); ?></code></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </form>

            <hr style="margin: 32px 0;">

            <h2><?php esc_html_e('Orphan Image Cleanup', 'comment-image-boost'); ?></h2>
            <p><?php esc_html_e('Files in the plugin upload folder that are not referenced by any comment meta can be removed here.', 'comment-image-boost'); ?></p>

            <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                <input type="hidden" name="action" value="ciboost_delete_orphans">
                <?php wp_nonce_field('ciboost_delete_orphans'); ?>
                <?php
                $orphan_count = count($orphans);
                /* translators: %d: number of orphan image files. */
                $orphan_message = sprintf(_n('%d orphan file found.', '%d orphan files found.', $orphan_count, 'comment-image-boost'), $orphan_count);
                ?>
                <p><strong><?php echo esc_html($orphan_message); ?></strong></p>
                <?php if (!empty($orphans)): ?>
                    <div style="max-height: 240px; overflow: auto; background: #fff; border: 1px solid #dcdcde; padding: 12px;">
                        <?php foreach ($orphans as $orphan): ?>
                            <label style="display: block; margin-bottom: 8px;">
                                <input type="checkbox" name="ciboost_orphan_files[]" value="<?php echo esc_attr($orphan); ?>" checked>
                                <code><?php echo esc_html($orphan); ?></code>
                            </label>
                        <?php endforeach; ?>
                    </div>
                    <?php submit_button(__('Delete selected orphan files', 'comment-image-boost'), 'delete'); ?>
                <?php endif; ?>
            </form>
        </div>
        <?php
    }

    private function update_image_status($comment_id, $filename, $status) {
        $comment_id = absint($comment_id);
        $filename = sanitize_file_name($filename);
        $status = sanitize_key($status);

        if (!$comment_id || $filename === '' || !in_array($status, array('approved', 'pending', 'rejected'), true)) {
            return;
        }

        $statuses = get_comment_meta($comment_id, CIBOOST_Comment_Images::STATUS_META_KEY, true);
        $statuses = is_array($statuses) ? $statuses : array();
        $statuses = array_map('sanitize_key', $statuses);
        $statuses[$filename] = $status;
        update_comment_meta($comment_id, CIBOOST_Comment_Images::STATUS_META_KEY, $statuses);
    }

    private function delete_image($comment_id, $filename) {
        $comment_id = absint($comment_id);
        $filename = sanitize_file_name($filename);

        if (!$comment_id || $filename === '') {
            return;
        }

        $image_data = get_comment_meta($comment_id, CIBOOST_Comment_Images::META_KEY, true);
        $images = is_array($image_data) ? $image_data : array($image_data);
        $images = array_values(array_filter(array_map('sanitize_file_name', $images)));

        $statuses = get_comment_meta($comment_id, CIBOOST_Comment_Images::STATUS_META_KEY, true);
        $captions = get_comment_meta($comment_id, CIBOOST_Comment_Images::CAPTIONS_META_KEY, true);
        $attachments = get_comment_meta($comment_id, CIBOOST_Comment_Images::ATTACHMENTS_META_KEY, true);

        $statuses = is_array($statuses) ? $statuses : array();
        $captions = is_array($captions) ? $captions : array();
        $attachments = is_array($attachments) ? $attachments : array();
        $statuses = array_map('sanitize_key', $statuses);
        $captions = array_map('sanitize_text_field', $captions);
        $attachments = array_map('absint', $attachments);

        if (!empty($attachments[$filename])) {
            wp_delete_attachment(absint($attachments[$filename]), true);
        }

        $file_path = ciboost_get_comment_images_upload_dir() . $filename;
        if (file_exists($file_path)) {
            wp_delete_file($file_path);
        }

        $images = array_values(array_diff($images, array($filename)));
        unset($statuses[$filename], $captions[$filename], $attachments[$filename]);

        if (!empty($images)) {
            update_comment_meta($comment_id, CIBOOST_Comment_Images::META_KEY, count($images) === 1 ? $images[0] : $images);
        } else {
            delete_comment_meta($comment_id, CIBOOST_Comment_Images::META_KEY);
        }

        !empty($statuses) ? update_comment_meta($comment_id, CIBOOST_Comment_Images::STATUS_META_KEY, $statuses) : delete_comment_meta($comment_id, CIBOOST_Comment_Images::STATUS_META_KEY);
        !empty($captions) ? update_comment_meta($comment_id, CIBOOST_Comment_Images::CAPTIONS_META_KEY, $captions) : delete_comment_meta($comment_id, CIBOOST_Comment_Images::CAPTIONS_META_KEY);
        !empty($attachments) ? update_comment_meta($comment_id, CIBOOST_Comment_Images::ATTACHMENTS_META_KEY, $attachments) : delete_comment_meta($comment_id, CIBOOST_Comment_Images::ATTACHMENTS_META_KEY);
    }

    public function handle_bulk_action() {
        if (!current_user_can('moderate_comments')) {
            wp_die(esc_html__('You do not have permission to moderate comment images.', 'comment-image-boost'));
        }

        check_admin_referer('ciboost_moderate_comment_images');

        $bulk_action = isset($_POST['ciboost_bulk_action']) ? sanitize_key(wp_unslash($_POST['ciboost_bulk_action'])) : '';
        $selected = isset($_POST['ciboost_images']) ? array_map('sanitize_text_field', (array) wp_unslash($_POST['ciboost_images'])) : array();

        if (!in_array($bulk_action, array('approved', 'pending', 'rejected', 'delete'), true)) {
            wp_safe_redirect(admin_url('admin.php?page=comment-image-boost-moderation'));
            exit;
        }

        foreach ($selected as $item) {
            $parts = explode('|', $item, 2); // Already sanitized at access
            if (count($parts) !== 2) {
                continue;
            }

            $comment_id = absint($parts[0]);
            $filename = sanitize_file_name($parts[1]);

            if (!$comment_id || $filename === '') {
                continue;
            }

            if ($bulk_action === 'delete') {
                $this->delete_image($comment_id, $filename);
            } else {
                $this->update_image_status($comment_id, $filename, $bulk_action);
            }
        }

        wp_safe_redirect(add_query_arg(
            array(
                'page' => 'comment-image-boost-moderation',
                'ciboost_updated' => 1,
                'ciboost_notice_nonce' => wp_create_nonce('ciboost_moderation_notice'),
            ),
            admin_url('admin.php')
        ));
        exit;
    }

    private function get_used_filenames() {
        $comments = get_comments(array(
            'status' => 'all',
            'number' => 0,
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required to compare stored image metadata with files on disk.
            'meta_key' => CIBOOST_Comment_Images::META_KEY,
        ));

        $used = array();
        foreach ($comments as $comment) {
            $image_data = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::META_KEY, true);
            $filenames = is_array($image_data) ? $image_data : array($image_data);
            $attachments = get_comment_meta($comment->comment_ID, CIBOOST_Comment_Images::ATTACHMENTS_META_KEY, true);
            $attachments = is_array($attachments) ? $attachments : array();

            foreach ($filenames as $filename) {
                $filename = sanitize_file_name($filename);
                if ($filename !== '') {
                    $used[$filename] = true;
                }
            }

            foreach ($attachments as $attachment_id) {
                $metadata = wp_get_attachment_metadata(absint($attachment_id));
                if (empty($metadata) || empty($metadata['sizes']) || !is_array($metadata['sizes'])) {
                    continue;
                }

                foreach ($metadata['sizes'] as $size) {
                    if (!empty($size['file'])) {
                        $used[sanitize_file_name($size['file'])] = true;
                    }
                }
            }
        }

        return $used;
    }

    private function scan_orphan_files() {
        $upload_dir = ciboost_get_comment_images_upload_dir();
        $files = array();
        foreach (array('jpg', 'jpeg', 'png', 'gif', 'webp', 'avif') as $extension) {
            $matches = glob($upload_dir . '*.' . $extension);
            if (is_array($matches)) {
                $files = array_merge($files, $matches);
            }
        }

        $used = $this->get_used_filenames();
        $orphans = array();

        if (!$files) {
            return $orphans;
        }

        foreach ($files as $file) {
            $filename = sanitize_file_name(basename($file));
            if (empty($used[$filename])) {
                $orphans[] = $filename;
            }
        }

        sort($orphans);
        return $orphans;
    }

    public function handle_delete_orphans() {
        if (!current_user_can('moderate_comments')) {
            wp_die(esc_html__('You do not have permission to delete orphan images.', 'comment-image-boost'));
        }

        check_admin_referer('ciboost_delete_orphans');

        $orphans = isset($_POST['ciboost_orphan_files']) ? array_map('sanitize_file_name', (array) wp_unslash($_POST['ciboost_orphan_files'])) : array();
        $known_orphans = array_flip($this->scan_orphan_files());
        $upload_dir = ciboost_get_comment_images_upload_dir();

        foreach ($orphans as $filename) {
            if (empty($known_orphans[$filename])) {
                continue;
            }

            $file_path = $upload_dir . $filename;
            if (file_exists($file_path)) {
                wp_delete_file($file_path);
            }
        }

        wp_safe_redirect(add_query_arg(
            array(
                'page' => 'comment-image-boost-moderation',
                'ciboost_updated' => 1,
                'ciboost_notice_nonce' => wp_create_nonce('ciboost_moderation_notice'),
            ),
            admin_url('admin.php')
        ));
        exit;
    }
}

$GLOBALS['ciboost_moderation_tools'] = new CIBOOST_Moderation_Tools();
