<?php
if (!defined('ABSPATH')) {
    exit;
}

class Calliope_SP_Media_Helper {
    /**
     * Decode a base64 image, save it to uploads, optionally convert it to a preferred
     * image MIME, and verify that the public URL serves a valid image.
     *
     * @param string $b64  Data URL or raw base64 image.
     * @param array  $args {
     *     Optional. Save/validation options.
     *
     *     @type string $context        Uploads subdirectory name.
     *     @type string $filename_prefix Base filename prefix.
     *     @type string $preferred_mime Preferred output MIME, for example image/jpeg.
     *     @type string $network        Network key used only for logs.
     *     @type int    $max_attempts   Public URL validation attempts.
     * }
     * @return array<string,mixed>|WP_Error
     */
    public static function create_public_image_from_b64($b64, array $args = []) {
        $args = wp_parse_args($args, [
            'context'         => 'shared',
            'filename_prefix' => 'calliope-social-publisher',
            'preferred_mime'  => '',
            'network'         => '',
            'max_attempts'    => 6,
            'url_strategy'        => 'rest_proxy',
            'url_ttl'             => 6 * HOUR_IN_SECONDS,
            'required_public_mime'=> '',
            'require_head_200'    => false,
            'allow_partial_get'   => true,
            'force_jpeg_reencode' => false,
            'skip_public_validation' => false,
            'trust_local_on_public_validation_failure' => false,
        ]);

        $decoded = self::decode_base64_image($b64);
        if (is_wp_error($decoded)) {
            return $decoded;
        }

        $uploads = wp_upload_dir();
        if (!empty($uploads['error'])) {
            return new WP_Error('upload_failed', 'Could not access the uploads directory: ' . $uploads['error']);
        }

        $context = sanitize_key((string) $args['context']);
        if ($context === '') {
            $context = 'shared';
        }

        $url_strategy = sanitize_key((string) $args['url_strategy']);
        if ($url_strategy === '') {
            $url_strategy = 'static_upload';
        }

        $use_meta_static_store = in_array($url_strategy, ['meta_static_safe', 'meta_static_passthrough'], true);

        $filename_prefix = sanitize_file_name((string) $args['filename_prefix']);
        if ($filename_prefix === '') {
            $filename_prefix = 'calliope-social-publisher';
        }

        $dir = trailingslashit($uploads['basedir']) . 'calliope-social-publisher/' . $context . '/';

        if (!wp_mkdir_p($dir)) {
            return new WP_Error('upload_failed', 'Could not create the uploads directory.');
        }

        $resolved_dir = self::resolve_plugin_media_directory($dir, 'media');
        if (is_wp_error($resolved_dir)) {
            return new WP_Error('upload_failed', 'Could not create a safe uploads directory.');
        }

        $filename = $filename_prefix . '-' . gmdate('Ymd-His') . '-' . wp_generate_password(6, false, false) . '.' . $decoded['extension'];
        $target = self::resolve_plugin_media_target(trailingslashit((string) $resolved_dir['canonical_path']) . $filename, 'media');
        if (is_wp_error($target)) {
            return new WP_Error('upload_failed', 'Could not prepare a safe image destination.');
        }

        $path = (string) $target['canonical_path'];
        if (file_put_contents($path, $decoded['binary']) === false) {
            return new WP_Error('upload_failed', 'Could not write the image file to uploads.');
        }

        $written_file = self::resolve_plugin_media_file($path, true, 'media');
        if (is_wp_error($written_file)) {
            self::delete_file($path);
            return new WP_Error('upload_failed', 'Could not validate the generated image file.');
        }
        $path = (string) $written_file['canonical_path'];

        $preferred_mime = strtolower((string) $args['preferred_mime']);
        $converted_to_preferred_mime = false;
        if ($preferred_mime !== '' && $preferred_mime !== strtolower((string) $decoded['mime'])) {
            $converted = self::convert_image_file($path, $preferred_mime);
            if (is_wp_error($converted)) {
                self::delete_file($path);
                return $converted;
            }
            $path = $converted['path'];
            $converted_to_preferred_mime = true;
        }

        $force_jpeg_reencode = !empty($args['force_jpeg_reencode']) || ($use_meta_static_store && $preferred_mime === 'image/jpeg');
        if ($force_jpeg_reencode && $preferred_mime === 'image/jpeg' && !$converted_to_preferred_mime) {
            $reencoded = self::reencode_image_file($path, 'image/jpeg');
            if (is_wp_error($reencoded)) {
                self::delete_file($path);
                return $reencoded;
            }
            $path = $reencoded['path'];
        }

        $file_meta = self::inspect_image_file($path);
        if (is_wp_error($file_meta)) {
            self::delete_file($path);
            return $file_meta;
        }

        if ($preferred_mime === 'image/jpeg' && !self::file_starts_with_jpeg_magic($path)) {
            self::delete_file($path);
            return new WP_Error('invalid_image_file', 'The generated JPEG file does not start with valid JPEG bytes.');
        }

        $static_url = self::build_public_url_from_upload_path($path);
        if (is_wp_error($static_url)) {
            self::delete_file($path);
            return $static_url;
        }
        $static_url = (string) $static_url;

        $meta_static_path = '';
        $meta_static_url = '';
        $meta_file_meta = [];
        $passthrough_url = '';

        if ($use_meta_static_store) {
            $meta_copy = self::copy_image_to_meta_static_store($path, $context, $filename_prefix);
            if (is_wp_error($meta_copy)) {
                self::delete_file($path);
                return $meta_copy;
            }

            $meta_static_path = (string) ($meta_copy['path'] ?? '');
            $meta_static_url = (string) ($meta_copy['url'] ?? '');
            $meta_file_meta = isset($meta_copy['meta']) && is_array($meta_copy['meta']) ? $meta_copy['meta'] : [];

            if ($url_strategy === 'meta_static_passthrough') {
                $passthrough = self::create_meta_static_proxy_url(
                    $meta_static_path,
                    $context,
                    $filename_prefix,
                    max(300, (int) $args['url_ttl'])
                );
                $passthrough_url = is_wp_error($passthrough) ? '' : (string) $passthrough;
            }
        }

        $proxy_path = $meta_static_path !== '' ? $meta_static_path : $path;
        $proxy_meta = !empty($meta_file_meta) ? $meta_file_meta : $file_meta;
        $proxy_url = self::create_signed_public_media_url(
            $proxy_path,
            (string) ($proxy_meta['mime'] ?? 'image/jpeg'),
            max(300, (int) $args['url_ttl'])
        );
        $proxy_url = is_wp_error($proxy_url) ? '' : (string) $proxy_url;

        $candidates = [];
        if ($url_strategy === 'meta_static_passthrough') {
            if ($passthrough_url !== '') {
                $candidates[] = [
                    'url'       => $passthrough_url,
                    'strategy'  => 'meta_static_passthrough',
                    'path'      => $meta_static_path,
                    'file_meta' => $meta_file_meta,
                ];
            }
            if ($meta_static_url !== '') {
                $candidates[] = [
                    'url'       => $meta_static_url,
                    'strategy'  => 'meta_static_safe',
                    'path'      => $meta_static_path,
                    'file_meta' => $meta_file_meta,
                ];
            }
            if ($static_url !== '' && $static_url !== $meta_static_url) {
                $candidates[] = [
                    'url'       => $static_url,
                    'strategy'  => 'static_upload',
                    'path'      => $path,
                    'file_meta' => $file_meta,
                ];
            }
            if ($proxy_url !== '') {
                $candidates[] = [
                    'url'       => $proxy_url,
                    'strategy'  => 'rest_proxy',
                    'path'      => $proxy_path,
                    'file_meta' => $proxy_meta,
                ];
            }
        } elseif ($url_strategy === 'meta_static_safe') {
            if ($meta_static_url !== '') {
                $candidates[] = [
                    'url'       => $meta_static_url,
                    'strategy'  => 'meta_static_safe',
                    'path'      => $meta_static_path,
                    'file_meta' => $meta_file_meta,
                ];
            }
            if ($static_url !== '' && $static_url !== $meta_static_url) {
                $candidates[] = [
                    'url'       => $static_url,
                    'strategy'  => 'static_upload',
                    'path'      => $path,
                    'file_meta' => $file_meta,
                ];
            }
            if ($proxy_url !== '') {
                $candidates[] = [
                    'url'       => $proxy_url,
                    'strategy'  => 'rest_proxy',
                    'path'      => $proxy_path,
                    'file_meta' => $proxy_meta,
                ];
            }
        } elseif ($url_strategy === 'rest_proxy' || $url_strategy === 'proxy_then_static') {
            if ($proxy_url !== '') {
                $candidates[] = ['url' => $proxy_url, 'strategy' => 'rest_proxy', 'path' => $proxy_path, 'file_meta' => $proxy_meta];
            }
            $candidates[] = ['url' => $static_url, 'strategy' => 'static_upload', 'path' => $path, 'file_meta' => $file_meta];
        } elseif ($url_strategy === 'static_then_proxy') {
            $candidates[] = ['url' => $static_url, 'strategy' => 'static_upload', 'path' => $path, 'file_meta' => $file_meta];
            if ($proxy_url !== '') {
                $candidates[] = ['url' => $proxy_url, 'strategy' => 'rest_proxy', 'path' => $proxy_path, 'file_meta' => $proxy_meta];
            }
        } else {
            $candidates[] = ['url' => $static_url, 'strategy' => 'static_upload', 'path' => $path, 'file_meta' => $file_meta];
            if ($url_strategy === 'static_upload_with_proxy_fallback' && $proxy_url !== '') {
                $candidates[] = ['url' => $proxy_url, 'strategy' => 'rest_proxy', 'path' => $proxy_path, 'file_meta' => $proxy_meta];
            }
        }

        $network = sanitize_key((string) $args['network']);
        if ($network !== '' && class_exists('Calliope_SP_Logger')) {
            if ($network === 'pinterest') {
                Calliope_SP_Logger::log($network, 'info', wp_json_encode([
                    'message' => 'Pinterest media URL diagnostics',
                    'strategy' => $url_strategy,
                    'mime_type' => (string) ($file_meta['mime'] ?? ''),
                    'width' => (int) ($file_meta['width'] ?? 0),
                    'height' => (int) ($file_meta['height'] ?? 0),
                    'size_bytes' => (int) ($file_meta['filesize'] ?? 0),
                    'head_status' => 0,
                    'range_status' => 0,
                ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            } else {
                $prepared_log = array_merge([
                    'message'           => 'Meta media URL diagnostics: prepared local image and candidate public URLs.',
                    'local_file'        => basename($path),
                    'static_url'        => $static_url,
                    'meta_static_file'  => $meta_static_path !== '' ? basename($meta_static_path) : '',
                    'meta_static_url'   => $meta_static_url,
                    'passthrough_url'   => $passthrough_url,
                    'proxy_url'         => $proxy_url,
                    'requested_strategy'=> $url_strategy,
                    'required_public_mime' => (string) $args['required_public_mime'],
                    'require_head_200'  => (bool) $args['require_head_200'],
                    'allow_partial_get' => (bool) $args['allow_partial_get'],
                    'candidate_count'   => count($candidates),
                    'site_url'          => site_url('/'),
                    'home_url'          => home_url('/'),
                ], self::get_local_file_diagnostics($meta_static_path !== '' ? $meta_static_path : $path, !empty($meta_file_meta) ? $meta_file_meta : $file_meta));
                Calliope_SP_Logger::log($network, 'info', wp_json_encode($prepared_log, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            }
        }

        $url = $passthrough_url !== '' ? $passthrough_url : ($meta_static_url !== '' ? $meta_static_url : $static_url);
        $resolved_strategy = $passthrough_url !== '' ? 'meta_static_passthrough' : ($meta_static_url !== '' ? 'meta_static_safe' : 'static_upload');
        $selected_path = $meta_static_path !== '' ? $meta_static_path : $path;
        $selected_file_meta = !empty($meta_file_meta) ? $meta_file_meta : $file_meta;
        $public_meta = null;
        $last_error = null;
        $candidate_diagnostics = [];
        $skip_public_validation = !empty($args['skip_public_validation']);
        $trust_local_on_public_validation_failure = !empty($args['trust_local_on_public_validation_failure']);

        foreach ($candidates as $candidate) {
            $candidate_url = esc_url_raw((string) ($candidate['url'] ?? ''));
            $candidate_strategy = sanitize_key((string) ($candidate['strategy'] ?? 'static_upload'));
            if ($candidate_url === '') {
                continue;
            }

            $candidate_path = (string) ($candidate['path'] ?? '');
            $candidate_file_meta = isset($candidate['file_meta']) && is_array($candidate['file_meta']) ? $candidate['file_meta'] : [];
            $candidate_local_diagnostics = self::get_local_file_diagnostics($candidate_path, $candidate_file_meta);

            if ($skip_public_validation) {
                $url = $candidate_url;
                $resolved_strategy = $candidate_strategy;
                $selected_path = (string) ($candidate['path'] ?? $selected_path);
                $selected_file_meta = isset($candidate['file_meta']) && is_array($candidate['file_meta']) ? $candidate['file_meta'] : $selected_file_meta;
                $public_meta = self::build_trusted_local_public_meta($candidate_local_diagnostics, 'skipped');
                $candidate_diagnostics[] = [
                    'strategy' => $candidate_strategy,
                    'url'      => $candidate_url,
                    'local_file' => basename($selected_path),
                    'ok'       => true,
                    'trusted_local' => true,
                    'meta'     => $public_meta,
                ];
                break;
            }

            $candidate_meta = self::wait_for_public_image_url($candidate_url, (string) $args['network'], (int) $args['max_attempts'], [
                'required_mime'     => (string) $args['required_public_mime'],
                'require_head_200'  => (bool) $args['require_head_200'],
                'allow_partial_get' => (bool) $args['allow_partial_get'],
                'expected_content_length' => (int) ($candidate_local_diagnostics['filesize'] ?? 0),
                'expected_first_12_bytes_hex' => (string) ($candidate_local_diagnostics['first_12_bytes_hex'] ?? ''),
                'require_public_body_match' => $candidate_strategy === 'meta_static_passthrough',
            ]);
            if (!is_wp_error($candidate_meta)) {
                $url = $candidate_url;
                $resolved_strategy = $candidate_strategy;
                $selected_path = (string) ($candidate['path'] ?? $selected_path);
                $selected_file_meta = isset($candidate['file_meta']) && is_array($candidate['file_meta']) ? $candidate['file_meta'] : $selected_file_meta;
                $public_meta = $candidate_meta;
                $candidate_diagnostics[] = [
                    'strategy' => $candidate_strategy,
                    'url'      => $candidate_url,
                    'local_file' => basename($selected_path),
                    'ok'       => true,
                    'meta'     => $candidate_meta,
                ];
                break;
            }

            $candidate_diagnostics[] = [
                'strategy' => $candidate_strategy,
                'url'      => $candidate_url,
                'ok'       => false,
                'error'    => $candidate_meta->get_error_message(),
                'data'     => $candidate_meta->get_error_data(),
            ];
            $last_error = $candidate_meta;
        }

        if ($public_meta === null) {
            if ($trust_local_on_public_validation_failure && !empty($candidates)) {
                $trusted_candidate = $candidates[0];
                $url = esc_url_raw((string) ($trusted_candidate['url'] ?? $url));
                $resolved_strategy = sanitize_key((string) ($trusted_candidate['strategy'] ?? $resolved_strategy));
                $selected_path = (string) ($trusted_candidate['path'] ?? $selected_path);
                $selected_file_meta = isset($trusted_candidate['file_meta']) && is_array($trusted_candidate['file_meta']) ? $trusted_candidate['file_meta'] : $selected_file_meta;
                $public_meta = self::build_trusted_local_public_meta(self::get_local_file_diagnostics($selected_path, $selected_file_meta), 'self_validation_failed');

                if ($network !== '' && class_exists('Calliope_SP_Logger')) {
                    $safe_log = $network === 'pinterest' ? [
                        'message' => 'Pinterest media URL diagnostics',
                        'strategy' => $resolved_strategy,
                        'mime_type' => (string) ($selected_file_meta['mime'] ?? ''),
                        'width' => (int) ($selected_file_meta['width'] ?? 0),
                        'height' => (int) ($selected_file_meta['height'] ?? 0),
                        'size_bytes' => (int) ($selected_file_meta['filesize'] ?? 0),
                        'head_status' => 0,
                        'range_status' => 0,
                    ] : [
                        'message' => 'Public media self-check failed on this host, but the local JPEG is valid. Sending the direct public URL to the network instead of blocking the publish.',
                        'public_url' => $url,
                        'url_strategy' => $resolved_strategy,
                        'last_error_code' => $last_error instanceof WP_Error ? $last_error->get_error_code() : '',
                        'last_error_message' => $last_error instanceof WP_Error ? $last_error->get_error_message() : '',
                    ];
                    Calliope_SP_Logger::log($network, 'warning', wp_json_encode($safe_log, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
                }
            } else {
                $error_data = [
                    'local_file'        => basename($path),
                    'meta_static_file'  => $meta_static_path !== '' ? basename($meta_static_path) : '',
                    'meta_static_url'   => $meta_static_url,
                    'passthrough_url'   => $passthrough_url,
                    'static_url'        => $static_url,
                    'proxy_url'         => $proxy_url,
                    'requested_strategy'=> $url_strategy,
                    'candidates'        => $candidate_diagnostics,
                ];
                if ($last_error instanceof WP_Error) {
                    $error_data['last_error_code'] = $last_error->get_error_code();
                    $error_data['last_error_message'] = $last_error->get_error_message();
                    $error_data['last_error_data'] = $last_error->get_error_data();
                }
                return new WP_Error(
                    'invalid_public_media_url',
                    'The public media URL is not accessible or does not return a valid image.',
                    $error_data
                );
            }
        }

        $result = array_merge($selected_file_meta, self::get_local_file_diagnostics($selected_path, $selected_file_meta), [
            'url'               => esc_url_raw($url),
            'passthrough_url'   => esc_url_raw($passthrough_url),
            'meta_static_url'   => esc_url_raw($meta_static_url),
            'static_url'        => esc_url_raw($static_url),
            'url_strategy'      => $resolved_strategy,
            'local_path'        => $selected_path,
            'http_head_status'  => (int) ($public_meta['http_head_status'] ?? 0),
            'http_content_type' => (string) ($public_meta['http_content_type'] ?? ''),
            'http_get_status'   => (int) ($public_meta['http_get_status'] ?? 0),
            'http_content_length' => (string) ($public_meta['http_content_length'] ?? ''),
            'http_accept_ranges' => (string) ($public_meta['http_accept_ranges'] ?? ''),
            'http_cache_control' => (string) ($public_meta['http_cache_control'] ?? ''),
            'http_x_content_type_options' => (string) ($public_meta['http_x_content_type_options'] ?? ''),
            'http_server' => (string) ($public_meta['http_server'] ?? ''),
            'http_via' => (string) ($public_meta['http_via'] ?? ''),
            'http_cf_cache_status' => (string) ($public_meta['http_cf_cache_status'] ?? ''),
            'http_x_litespeed_cache' => (string) ($public_meta['http_x_litespeed_cache'] ?? ''),
            'http_x_hcdn_cache_status' => (string) ($public_meta['http_x_hcdn_cache_status'] ?? ''),
            'http_x_hcdn_request_id' => (string) ($public_meta['http_x_hcdn_request_id'] ?? ''),
            'body_starts_with_jpeg' => (bool) ($public_meta['body_starts_with_jpeg'] ?? false),
            'body_detected_mime' => (string) ($public_meta['body_detected_mime'] ?? ''),
            'body_magic' => (string) ($public_meta['body_magic'] ?? ''),
            'body_matches_local' => (bool) ($public_meta['body_matches_expected'] ?? false),
            'body_size_matches_local' => (bool) ($public_meta['body_size_matches_expected'] ?? false),
            'body_magic_matches_local' => (bool) ($public_meta['body_magic_matches_expected'] ?? false),
            'http_content_length_matches_local' => (bool) ($public_meta['http_content_length_matches_expected'] ?? false),
        ]);

        if ($network !== '' && class_exists('Calliope_SP_Logger')) {
            if ($network === 'pinterest') {
                Calliope_SP_Logger::log($network, $resolved_strategy === 'rest_proxy' ? 'warning' : 'info', wp_json_encode([
                    'message' => 'Pinterest media URL diagnostics',
                    'strategy' => $resolved_strategy === 'static_upload' && wp_parse_url($result['url'], PHP_URL_HOST) !== wp_parse_url(home_url('/'), PHP_URL_HOST) ? 'cdn' : $resolved_strategy,
                    'mime_type' => (string) ($result['mime'] ?? ''),
                    'width' => (int) ($result['width'] ?? 0),
                    'height' => (int) ($result['height'] ?? 0),
                    'size_bytes' => (int) ($result['filesize'] ?? 0),
                    'head_status' => (int) ($result['http_head_status'] ?? 0),
                    'range_status' => (int) ($result['http_get_status'] ?? 0),
                ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            } else {
                if ($resolved_strategy === 'rest_proxy') {
                    Calliope_SP_Logger::log($network, 'warning', wp_json_encode([
                        'message'      => 'Meta media URL diagnostics: falling back to REST proxy. Meta may reject wp-json media URLs.',
                        'public_url'   => $result['url'],
                        'url_strategy' => $resolved_strategy,
                        'static_url'   => $result['static_url'],
                        'meta_static_url' => $result['meta_static_url'],
                        'passthrough_url' => $result['passthrough_url'],
                    ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
                }

                Calliope_SP_Logger::log($network, 'info', wp_json_encode([
                    'message'           => 'Meta media URL diagnostics: selected public URL for API payload.',
                    'local_file'        => basename((string) $result['local_path']),
                    'public_url'        => $result['url'],
                    'passthrough_url'   => $result['passthrough_url'],
                    'meta_static_url'   => $result['meta_static_url'],
                    'static_url'        => $result['static_url'],
                    'url_strategy'      => $result['url_strategy'],
                    'mime'              => $result['mime'],
                    'width'             => $result['width'],
                    'height'            => $result['height'],
                    'filesize'          => $result['filesize'],
                    'first_12_bytes_hex'=> $result['first_12_bytes_hex'],
                    'sha1'              => $result['sha1'],
                    'http_head_status'  => $result['http_head_status'],
                    'http_content_type' => $result['http_content_type'],
                    'http_get_status'   => $result['http_get_status'],
                    'http_content_length' => $result['http_content_length'],
                    'http_accept_ranges' => $result['http_accept_ranges'],
                    'http_cache_control' => $result['http_cache_control'],
                    'http_x_content_type_options' => $result['http_x_content_type_options'],
                    'http_server' => $result['http_server'],
                    'http_via' => $result['http_via'],
                    'http_cf_cache_status' => $result['http_cf_cache_status'],
                    'http_x_litespeed_cache' => $result['http_x_litespeed_cache'],
                    'http_x_hcdn_cache_status' => $result['http_x_hcdn_cache_status'],
                    'http_x_hcdn_request_id' => $result['http_x_hcdn_request_id'],
                    'body_starts_with_jpeg' => $result['body_starts_with_jpeg'],
                    'body_detected_mime' => $result['body_detected_mime'],
                    'body_magic' => $result['body_magic'],
                    'body_matches_local' => $result['body_matches_local'],
                    'body_size_matches_local' => $result['body_size_matches_local'],
                    'body_magic_matches_local' => $result['body_magic_matches_local'],
                    'http_content_length_matches_local' => $result['http_content_length_matches_local'],
                ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

            }
        }
        return $result;
    }

    /**
     * Build synthetic public metadata when the local file is already valid but the
     * host cannot self-fetch its own HTTPS URL (common on some shared hosts/CDNs).
     * This avoids blocking Meta/Telegram before the external network gets a chance
     * to fetch the media itself.
     *
     * @param array<string,mixed> $local_diagnostics Local file diagnostics.
     * @param string              $reason Reason this trusted result was used.
     * @return array<string,mixed>
     */
    private static function build_trusted_local_public_meta(array $local_diagnostics, $reason = '') {
        $mime = (string) ($local_diagnostics['mime'] ?? '');
        $filesize = (int) ($local_diagnostics['filesize'] ?? 0);

        return [
            'trusted_local' => true,
            'trusted_local_reason' => sanitize_key((string) $reason),
            'http_head_status' => 0,
            'http_head_type' => '',
            'http_head_length' => '',
            'http_head_location' => '',
            'http_content_type' => $mime,
            'http_content_length' => $filesize > 0 ? (string) $filesize : '',
            'http_get_status' => 0,
            'http_get_type' => '',
            'http_get_length' => '',
            'body_bytes' => $filesize,
            'body_detected_mime' => $mime,
            'body_magic' => (string) ($local_diagnostics['first_12_bytes_hex'] ?? ''),
            'body_starts_with_jpeg' => !empty($local_diagnostics['starts_with_jpeg_magic']),
            'body_matches_expected' => true,
            'body_size_matches_expected' => true,
            'body_magic_matches_expected' => true,
            'http_content_length_matches_expected' => true,
            'http_accept_ranges' => '',
            'http_cache_control' => '',
            'http_x_content_type_options' => '',
            'http_server' => '',
            'http_via' => '',
            'http_cf_cache_status' => '',
            'http_x_litespeed_cache' => '',
            'http_x_hcdn_cache_status' => '',
            'http_x_hcdn_request_id' => '',
            'attempts' => [],
        ];
    }

    /**
     * Wait until a public image URL responds with an image content type.
     *
     * @param string $url          Public media URL.
     * @param string $network      Network key used only for logs.
     * @param int    $max_attempts Number of validation attempts.
     * @return array<string,mixed>|WP_Error
     */
    public static function wait_for_public_image_url($url, $network = '', $max_attempts = 6, array $args = []) {
        $args = wp_parse_args($args, [
            'required_mime'     => '',
            'require_head_200'  => false,
            'allow_partial_get' => true,
            'expected_content_length' => 0,
            'expected_first_12_bytes_hex' => '',
            'require_public_body_match' => false,
        ]);

        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return new WP_Error('invalid_public_media_url', 'Missing public media URL.');
        }

        $required_mime = self::normalize_content_type((string) $args['required_mime']);
        $require_head_200 = (bool) $args['require_head_200'];
        $allow_partial_get = (bool) $args['allow_partial_get'];
        $expected_content_length = max(0, (int) $args['expected_content_length']);
        $expected_first_12_bytes_hex = strtolower(preg_replace('/[^a-f0-9]/', '', (string) $args['expected_first_12_bytes_hex']));
        $require_public_body_match = (bool) $args['require_public_body_match'];

        $last_meta = [
            'public_url'            => $url,
            'required_mime'         => $required_mime,
            'require_head_200'      => $require_head_200,
            'allow_partial_get'     => $allow_partial_get,
            'expected_content_length' => $expected_content_length,
            'expected_first_12_bytes_hex' => $expected_first_12_bytes_hex,
            'require_public_body_match' => $require_public_body_match,
            'http_head_status'      => 0,
            'http_head_type'        => '',
            'http_head_length'      => '',
            'http_head_location'    => '',
            'http_head_error'       => '',
            'http_head_accept_ranges' => '',
            'http_head_cache_control' => '',
            'http_head_x_content_type_options' => '',
            'http_head_server'      => '',
            'http_head_via'         => '',
            'http_head_cf_cache_status' => '',
            'http_head_x_litespeed_cache' => '',
            'http_head_x_hcdn_cache_status' => '',
            'http_head_x_hcdn_request_id' => '',
            'http_content_type'     => '',
            'http_content_length'   => '',
            'http_accept_ranges'    => '',
            'http_cache_control'    => '',
            'http_x_content_type_options' => '',
            'http_server'           => '',
            'http_via'              => '',
            'http_cf_cache_status'  => '',
            'http_x_litespeed_cache'=> '',
            'http_x_hcdn_cache_status' => '',
            'http_x_hcdn_request_id'=> '',
            'http_get_status'       => 0,
            'http_get_type'         => '',
            'http_get_length'       => '',
            'http_get_location'     => '',
            'http_get_error'        => '',
            'http_get_accept_ranges' => '',
            'http_get_cache_control' => '',
            'http_get_x_content_type_options' => '',
            'http_get_server'       => '',
            'http_get_via'          => '',
            'http_get_cf_cache_status' => '',
            'http_get_x_litespeed_cache' => '',
            'http_get_x_hcdn_cache_status' => '',
            'http_get_x_hcdn_request_id' => '',
            'body_preview'          => '',
            'body_bytes'            => 0,
            'body_detected_mime'    => '',
            'body_magic'            => '',
            'body_starts_with_jpeg' => false,
            'body_matches_expected' => false,
            'body_size_matches_expected' => false,
            'body_magic_matches_expected' => false,
            'http_content_length_matches_expected' => false,
            'attempts'              => [],
        ];

        $accept = $required_mime === 'image/jpeg'
            ? 'image/jpeg,*/*;q=0.8'
            : 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8';

        $request_args = [
            'timeout'     => 20,
            'redirection' => 3,
            'user-agent'  => 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
            'headers'     => [
                'Accept'        => $accept,
                'Cache-Control' => 'no-cache',
                'Pragma'        => 'no-cache',
            ],
        ];

        for ($attempt = 1; $attempt <= max(1, (int) $max_attempts); $attempt++) {
            $attempt_meta = [
                'attempt'            => $attempt,
                'head_status'        => 0,
                'head_type'          => '',
                'head_normalized'    => '',
                'head_length'        => '',
                'head_location'      => '',
                'head_error'         => '',
                'head_accept_ranges' => '',
                'head_cache_control' => '',
                'head_x_content_type_options' => '',
                'head_server'        => '',
                'head_via'           => '',
                'head_cf_cache_status' => '',
                'head_x_litespeed_cache' => '',
                'head_x_hcdn_cache_status' => '',
                'head_x_hcdn_request_id' => '',
                'head_ok'            => false,
                'get_status'         => 0,
                'get_type'           => '',
                'get_normalized'     => '',
                'get_length'         => '',
                'get_location'       => '',
                'get_error'          => '',
                'get_accept_ranges'  => '',
                'get_cache_control'  => '',
                'get_x_content_type_options' => '',
                'get_server'         => '',
                'get_via'            => '',
                'get_cf_cache_status' => '',
                'get_x_litespeed_cache' => '',
                'get_x_hcdn_cache_status' => '',
                'get_x_hcdn_request_id' => '',
                'get_body_bytes'     => 0,
                'get_body_preview'   => '',
                'get_body_detected'  => '',
                'get_body_magic'     => '',
                'get_body_starts_with_jpeg' => false,
                'get_body_matches_expected' => false,
                'get_body_size_matches_expected' => false,
                'get_body_magic_matches_expected' => false,
                'get_content_length_matches_expected' => false,
                'get_ok'             => false,
            ];

            $head = wp_remote_head($url, $request_args);
            if (!is_wp_error($head)) {
                $attempt_meta['head_status']     = (int) wp_remote_retrieve_response_code($head);
                $attempt_meta['head_type']       = strtolower((string) wp_remote_retrieve_header($head, 'content-type'));
                $attempt_meta['head_normalized'] = self::normalize_content_type($attempt_meta['head_type']);
                $attempt_meta['head_length']     = (string) wp_remote_retrieve_header($head, 'content-length');
                $attempt_meta['head_location']   = (string) wp_remote_retrieve_header($head, 'location');
                $attempt_meta['head_accept_ranges'] = (string) wp_remote_retrieve_header($head, 'accept-ranges');
                $attempt_meta['head_cache_control'] = (string) wp_remote_retrieve_header($head, 'cache-control');
                $attempt_meta['head_x_content_type_options'] = (string) wp_remote_retrieve_header($head, 'x-content-type-options');
                $attempt_meta['head_server'] = (string) wp_remote_retrieve_header($head, 'server');
                $attempt_meta['head_via'] = (string) wp_remote_retrieve_header($head, 'via');
                $attempt_meta['head_cf_cache_status'] = (string) wp_remote_retrieve_header($head, 'cf-cache-status');
                $attempt_meta['head_x_litespeed_cache'] = (string) wp_remote_retrieve_header($head, 'x-litespeed-cache');
                $attempt_meta['head_x_hcdn_cache_status'] = (string) wp_remote_retrieve_header($head, 'x-hcdn-cache-status');
                $attempt_meta['head_x_hcdn_request_id'] = (string) wp_remote_retrieve_header($head, 'x-hcdn-request-id');

                $last_meta['http_head_status']   = $attempt_meta['head_status'];
                $last_meta['http_head_type']     = $attempt_meta['head_type'];
                $last_meta['http_head_length']   = $attempt_meta['head_length'];
                $last_meta['http_head_location'] = $attempt_meta['head_location'];
                $last_meta['http_head_accept_ranges'] = $attempt_meta['head_accept_ranges'];
                $last_meta['http_head_cache_control'] = $attempt_meta['head_cache_control'];
                $last_meta['http_head_x_content_type_options'] = $attempt_meta['head_x_content_type_options'];
                $last_meta['http_head_server'] = $attempt_meta['head_server'];
                $last_meta['http_head_via'] = $attempt_meta['head_via'];
                $last_meta['http_head_cf_cache_status'] = $attempt_meta['head_cf_cache_status'];
                $last_meta['http_head_x_litespeed_cache'] = $attempt_meta['head_x_litespeed_cache'];
                $last_meta['http_head_x_hcdn_cache_status'] = $attempt_meta['head_x_hcdn_cache_status'];
                $last_meta['http_head_x_hcdn_request_id'] = $attempt_meta['head_x_hcdn_request_id'];
                $last_meta['http_content_type']  = $attempt_meta['head_type'];
                $last_meta['http_content_length']= $attempt_meta['head_length'];
                $last_meta['http_accept_ranges'] = $attempt_meta['head_accept_ranges'];
                $last_meta['http_cache_control'] = $attempt_meta['head_cache_control'];
                $last_meta['http_x_content_type_options'] = $attempt_meta['head_x_content_type_options'];
                $last_meta['http_server'] = $attempt_meta['head_server'];
                $last_meta['http_via'] = $attempt_meta['head_via'];
                $last_meta['http_cf_cache_status'] = $attempt_meta['head_cf_cache_status'];
                $last_meta['http_x_litespeed_cache'] = $attempt_meta['head_x_litespeed_cache'];
                $last_meta['http_x_hcdn_cache_status'] = $attempt_meta['head_x_hcdn_cache_status'];
                $last_meta['http_x_hcdn_request_id'] = $attempt_meta['head_x_hcdn_request_id'];

                $attempt_meta['head_ok'] = (
                    $attempt_meta['head_status'] === 200 &&
                    self::is_allowed_public_content_type($attempt_meta['head_type'], $required_mime) &&
                    ($attempt_meta['head_length'] === '' || (int) $attempt_meta['head_length'] > 0)
                );

                if ($attempt_meta['head_ok'] && !$require_head_200) {
                    $last_meta['attempts'][] = $attempt_meta;
                    return $last_meta;
                }
            } else {
                $attempt_meta['head_error'] = $head->get_error_message();
                $last_meta['http_head_error'] = $attempt_meta['head_error'];
            }

            $get_args = $request_args;
            if ($allow_partial_get) {
                $get_args['headers'] = array_merge($request_args['headers'], [
                    'Range' => 'bytes=0-2047',
                ]);
            }

            $get = wp_remote_get($url, $get_args);

            if (!is_wp_error($get)) {
                $attempt_meta['get_status']     = (int) wp_remote_retrieve_response_code($get);
                $attempt_meta['get_type']       = strtolower((string) wp_remote_retrieve_header($get, 'content-type'));
                $attempt_meta['get_normalized'] = self::normalize_content_type($attempt_meta['get_type']);
                $attempt_meta['get_length']     = (string) wp_remote_retrieve_header($get, 'content-length');
                $attempt_meta['get_location']   = (string) wp_remote_retrieve_header($get, 'location');
                $attempt_meta['get_accept_ranges'] = (string) wp_remote_retrieve_header($get, 'accept-ranges');
                $attempt_meta['get_cache_control'] = (string) wp_remote_retrieve_header($get, 'cache-control');
                $attempt_meta['get_x_content_type_options'] = (string) wp_remote_retrieve_header($get, 'x-content-type-options');
                $attempt_meta['get_server'] = (string) wp_remote_retrieve_header($get, 'server');
                $attempt_meta['get_via'] = (string) wp_remote_retrieve_header($get, 'via');
                $attempt_meta['get_cf_cache_status'] = (string) wp_remote_retrieve_header($get, 'cf-cache-status');
                $attempt_meta['get_x_litespeed_cache'] = (string) wp_remote_retrieve_header($get, 'x-litespeed-cache');
                $attempt_meta['get_x_hcdn_cache_status'] = (string) wp_remote_retrieve_header($get, 'x-hcdn-cache-status');
                $attempt_meta['get_x_hcdn_request_id'] = (string) wp_remote_retrieve_header($get, 'x-hcdn-request-id');
                $body = (string) wp_remote_retrieve_body($get);
                $attempt_meta['get_body_bytes']    = strlen($body);
                $attempt_meta['get_body_detected'] = self::detect_image_mime_from_binary($body);
                $attempt_meta['get_body_magic']    = bin2hex(substr($body, 0, 12));
                $attempt_meta['get_body_starts_with_jpeg'] = strncmp($body, "\xFF\xD8\xFF", 3) === 0;
                $attempt_meta['get_body_size_matches_expected'] = $expected_content_length <= 0 || $attempt_meta['get_body_bytes'] === $expected_content_length;
                $attempt_meta['get_content_length_matches_expected'] = $expected_content_length <= 0 || $attempt_meta['get_length'] === '' || (int) $attempt_meta['get_length'] === $expected_content_length;
                $attempt_meta['get_body_magic_matches_expected'] = $expected_first_12_bytes_hex === '' || strtolower($attempt_meta['get_body_magic']) === substr($expected_first_12_bytes_hex, 0, 24);
                $attempt_meta['get_body_matches_expected'] = $attempt_meta['get_body_size_matches_expected'] && $attempt_meta['get_body_magic_matches_expected'];

                if (!self::is_allowed_public_content_type($attempt_meta['get_type'], $required_mime)) {
                    $attempt_meta['get_body_preview'] = self::preview_response_body($body);
                }

                $last_meta['http_get_status']     = $attempt_meta['get_status'];
                $last_meta['http_get_type']       = $attempt_meta['get_type'];
                $last_meta['http_get_length']     = $attempt_meta['get_length'];
                $last_meta['http_get_location']   = $attempt_meta['get_location'];
                $last_meta['http_get_accept_ranges'] = $attempt_meta['get_accept_ranges'];
                $last_meta['http_get_cache_control'] = $attempt_meta['get_cache_control'];
                $last_meta['http_get_x_content_type_options'] = $attempt_meta['get_x_content_type_options'];
                $last_meta['http_get_server'] = $attempt_meta['get_server'];
                $last_meta['http_get_via'] = $attempt_meta['get_via'];
                $last_meta['http_get_cf_cache_status'] = $attempt_meta['get_cf_cache_status'];
                $last_meta['http_get_x_litespeed_cache'] = $attempt_meta['get_x_litespeed_cache'];
                $last_meta['http_get_x_hcdn_cache_status'] = $attempt_meta['get_x_hcdn_cache_status'];
                $last_meta['http_get_x_hcdn_request_id'] = $attempt_meta['get_x_hcdn_request_id'];
                $last_meta['http_content_type']   = $attempt_meta['get_type'];
                $last_meta['http_content_length'] = $attempt_meta['get_length'];
                $last_meta['http_accept_ranges'] = $attempt_meta['get_accept_ranges'] !== '' ? $attempt_meta['get_accept_ranges'] : $last_meta['http_accept_ranges'];
                $last_meta['http_cache_control'] = $attempt_meta['get_cache_control'] !== '' ? $attempt_meta['get_cache_control'] : $last_meta['http_cache_control'];
                $last_meta['http_x_content_type_options'] = $attempt_meta['get_x_content_type_options'] !== '' ? $attempt_meta['get_x_content_type_options'] : $last_meta['http_x_content_type_options'];
                $last_meta['http_server'] = $attempt_meta['get_server'] !== '' ? $attempt_meta['get_server'] : $last_meta['http_server'];
                $last_meta['http_via'] = $attempt_meta['get_via'] !== '' ? $attempt_meta['get_via'] : $last_meta['http_via'];
                $last_meta['http_cf_cache_status'] = $attempt_meta['get_cf_cache_status'] !== '' ? $attempt_meta['get_cf_cache_status'] : $last_meta['http_cf_cache_status'];
                $last_meta['http_x_litespeed_cache'] = $attempt_meta['get_x_litespeed_cache'] !== '' ? $attempt_meta['get_x_litespeed_cache'] : $last_meta['http_x_litespeed_cache'];
                $last_meta['http_x_hcdn_cache_status'] = $attempt_meta['get_x_hcdn_cache_status'] !== '' ? $attempt_meta['get_x_hcdn_cache_status'] : $last_meta['http_x_hcdn_cache_status'];
                $last_meta['http_x_hcdn_request_id'] = $attempt_meta['get_x_hcdn_request_id'] !== '' ? $attempt_meta['get_x_hcdn_request_id'] : $last_meta['http_x_hcdn_request_id'];
                $last_meta['body_bytes']          = $attempt_meta['get_body_bytes'];
                $last_meta['body_preview']        = $attempt_meta['get_body_preview'];
                $last_meta['body_detected_mime']  = $attempt_meta['get_body_detected'];
                $last_meta['body_magic']          = $attempt_meta['get_body_magic'];
                $last_meta['body_starts_with_jpeg'] = $attempt_meta['get_body_starts_with_jpeg'];
                $last_meta['body_matches_expected'] = $attempt_meta['get_body_matches_expected'];
                $last_meta['body_size_matches_expected'] = $attempt_meta['get_body_size_matches_expected'];
                $last_meta['body_magic_matches_expected'] = $attempt_meta['get_body_magic_matches_expected'];
                $last_meta['http_content_length_matches_expected'] = $attempt_meta['get_content_length_matches_expected'];

                $valid_statuses = $allow_partial_get ? [200, 206] : [200];
                $attempt_meta['get_ok'] = (
                    in_array($attempt_meta['get_status'], $valid_statuses, true) &&
                    self::is_allowed_public_content_type($attempt_meta['get_type'], $required_mime) &&
                    ($required_mime === '' || $attempt_meta['get_body_detected'] === '' || $attempt_meta['get_body_detected'] === $required_mime) &&
                    ($required_mime !== 'image/jpeg' || $attempt_meta['get_body_starts_with_jpeg']) &&
                    (!$require_public_body_match || $attempt_meta['get_body_matches_expected']) &&
                    $body !== ''
                );

                if ($attempt_meta['get_ok'] && (!$require_head_200 || $attempt_meta['head_ok'])) {
                    $last_meta['attempts'][] = $attempt_meta;
                    return $last_meta;
                }
            } else {
                $attempt_meta['get_error'] = $get->get_error_message();
                $last_meta['http_get_error'] = $attempt_meta['get_error'];
            }

            $last_meta['attempts'][] = $attempt_meta;
            sleep(min(4, $attempt));
        }

        $network = sanitize_key((string) $network);
        if ($network !== '' && class_exists('Calliope_SP_Logger')) {
            $failure_log = $network === 'pinterest' ? [
                'message' => 'Pinterest media URL diagnostics',
                'strategy' => 'validation_failed',
                'mime_type' => (string) ($last_meta['http_content_type'] ?? ''),
                'width' => 0,
                'height' => 0,
                'size_bytes' => 0,
                'head_status' => (int) ($last_meta['http_head_status'] ?? 0),
                'range_status' => (int) ($last_meta['http_get_status'] ?? 0),
            ] : array_merge([
                'message' => 'Meta media URL diagnostics: public image URL validation failed before sending to API.',
                'public_url' => $url,
            ], $last_meta);
            Calliope_SP_Logger::log($network, 'error', wp_json_encode($failure_log, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        }

        return new WP_Error(
            'invalid_public_media_url',
            'The public media URL is not accessible or does not return a valid image that meets the required media MIME.',
            $last_meta
        );
    }

    /**
     * Build a safe, short preview of a non-image HTTP body for diagnostics.
     *
     * @param string $body Response body.
     * @return string
     */
    private static function preview_response_body($body) {
        $body = (string) $body;
        if ($body === '') {
            return '';
        }

        $snippet = substr($body, 0, 500);
        $snippet = preg_replace('/[^\P{C}\t\r\n]+/u', '', $snippet);
        if (!is_string($snippet)) {
            $snippet = substr($body, 0, 200);
        }
        $snippet = wp_strip_all_tags($snippet);
        $snippet = preg_replace('/\s+/', ' ', (string) $snippet);
        return trim(mb_substr((string) $snippet, 0, 300));
    }

    /**
     * Return local image metadata.
     *
     * @param string $path Absolute file path.
     * @return array<string,mixed>|WP_Error
     */
    public static function inspect_image_file($path) {
        $path = (string) $path;
        if ($path === '' || !file_exists($path) || !is_file($path)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }

        $size = @getimagesize($path);
        if (!is_array($size) || empty($size[0]) || empty($size[1])) {
            return new WP_Error('invalid_image_file', 'The generated file is not a valid image.');
        }

        $mime = '';
        if (!empty($size['mime'])) {
            $mime = strtolower((string) $size['mime']);
        }

        if ($mime === '' && function_exists('mime_content_type')) {
            $mime = strtolower((string) @mime_content_type($path));
        }

        if ($mime === '' && class_exists('finfo')) {
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            $mime = strtolower((string) $finfo->file($path));
        }

        $wp_type = wp_check_filetype(basename($path));
        if ($mime === '' && !empty($wp_type['type'])) {
            $mime = strtolower((string) $wp_type['type']);
        }

        if (!self::is_image_content_type($mime)) {
            return new WP_Error('invalid_image_file', 'The generated file does not report a valid image MIME type.');
        }

        return [
            'path'      => $path,
            'width'     => (int) $size[0],
            'height'    => (int) $size[1],
            'filesize'  => (int) filesize($path),
            'mime'      => $mime,
            'extension' => !empty($wp_type['ext']) ? sanitize_key((string) $wp_type['ext']) : self::guess_extension_from_mime($mime),
        ];
    }

    /**
     * Return the shared media-preparation arguments for Instagram and Threads.
     *
     * @param string $network Network key.
     * @return array<string,mixed>
     */
    public static function get_meta_static_image_args($network) {
        $network = sanitize_key((string) $network);
        $context = $network === 'threads' ? 'threads' : 'instagram';

        return [
            'context'              => $context,
            'filename_prefix'      => $context === 'threads' ? 'csp-meta-threads' : 'csp-meta-ig',
            'preferred_mime'       => 'image/jpeg',
            'network'              => $context,
            'max_attempts'         => 2,
            'url_strategy'         => 'meta_static_safe',
            'required_public_mime' => 'image/jpeg',
            'require_head_200'     => true,
            'allow_partial_get'    => false,
            'force_jpeg_reencode'  => true,
            'trust_local_on_public_validation_failure' => true,
        ];
    }

    /**
     * Create a simple 1080x1080 JPEG data URL for admin media URL diagnostics.
     *
     * @return string|WP_Error
     */
    public static function create_sample_jpeg_data_url() {
        if (!function_exists('imagecreatetruecolor') || !function_exists('imagejpeg')) {
            return new WP_Error('image_editor_missing', 'The server cannot generate a sample JPEG because the GD image library is unavailable.');
        }

        $size = 1080;
        $image = imagecreatetruecolor($size, $size);
        if (!$image) {
            return new WP_Error('image_editor_missing', 'The server could not allocate a sample JPEG image.');
        }

        $bg = imagecolorallocate($image, 244, 247, 250);
        $ink = imagecolorallocate($image, 24, 34, 79);
        $accent = imagecolorallocate($image, 214, 41, 118);
        $soft = imagecolorallocate($image, 235, 238, 245);

        imagefilledrectangle($image, 0, 0, $size, $size, $bg);
        imagefilledellipse($image, 540, 540, 760, 760, $soft);
        imagefilledrectangle($image, 250, 250, 830, 830, $accent);
        imagefilledrectangle($image, 310, 310, 770, 770, $bg);
        imagefilledellipse($image, 540, 540, 250, 250, $ink);

        ob_start();
        imagejpeg($image, null, 90);
        $binary = (string) ob_get_clean();
        imagedestroy($image);

        if ($binary === '' || strncmp($binary, "\xFF\xD8\xFF", 3) !== 0) {
            return new WP_Error('image_save_failed', 'The server could not generate a valid sample JPEG.');
        }

        return 'data:image/jpeg;base64,' . base64_encode($binary);
    }

    /**
     * Build local-file diagnostics for Meta media logs.
     *
     * @param string              $path Absolute local path.
     * @param array<string,mixed> $meta Optional image metadata.
     * @return array<string,mixed>
     */
    public static function get_local_file_diagnostics($path, array $meta = []) {
        $resolved = self::resolve_plugin_media_file($path, true);
        $safe_path = is_wp_error($resolved) ? '' : (string) $resolved['canonical_path'];
        $exists = $safe_path !== '';

        return [
            'file_exists'            => $exists,
            'file_readable'          => $exists && is_readable($safe_path),
            'filesize'               => $exists ? (int) filesize($safe_path) : 0,
            'mime'                   => (string) ($meta['mime'] ?? ''),
            'width'                  => (int) ($meta['width'] ?? 0),
            'height'                 => (int) ($meta['height'] ?? 0),
            'first_12_bytes_hex'     => $exists ? self::read_file_prefix_hex($safe_path, 12) : '',
            'sha1'                   => $exists ? (string) sha1_file($safe_path) : '',
            'starts_with_jpeg_magic' => $exists ? self::file_starts_with_jpeg_magic($safe_path) : false,
        ];
    }

    /**
     * Build the direct public uploads URL for a generated image file.
     *
     * @param string $path Absolute local path.
     * @return string|WP_Error
     */
    public static function build_public_url_from_upload_path($path) {
        $resolved = self::resolve_plugin_media_file($path, true);
        if (is_wp_error($resolved)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }

        $uploads = wp_upload_dir();
        if (!empty($uploads['error'])) {
            return new WP_Error('upload_failed', 'Could not access the uploads directory: ' . $uploads['error']);
        }

        $root_folder = $resolved['root_key'] === 'meta'
            ? 'calliope-social-publisher-meta'
            : 'calliope-social-publisher';
        $encoded_relative = self::encode_safe_relative_url_path((string) $resolved['relative_path']);
        if (is_wp_error($encoded_relative)) {
            return $encoded_relative;
        }

        $url = trailingslashit((string) $uploads['baseurl']) . rawurlencode($root_folder) . '/' . $encoded_relative;
        return esc_url_raw(self::normalize_public_url($url));
    }

    /**
     * Keep media URLs on HTTPS when the site itself is HTTPS.
     * Meta's media fetchers are stricter with redirects and mixed schemes than WordPress' own self-check.
     *
     * @param string $url Public URL.
     * @return string
     */
    private static function normalize_public_url($url) {
        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return '';
        }

        $home = home_url('/');
        if (is_ssl() || stripos((string) $home, 'https://') === 0) {
            $url = set_url_scheme($url, 'https');
        }

        return esc_url_raw($url);
    }

    /**
     * Delete a file created by the helper when it is no longer needed.
     *
     * @param string $path Absolute file path.
     * @return void
     */
    public static function delete_file($path) {
        $resolved = self::resolve_plugin_media_file($path, false);
        if (is_wp_error($resolved)) {
            return;
        }

        // Revalidate immediately before deletion to reduce the filesystem race window.
        $confirmed = self::resolve_plugin_media_file((string) $resolved['canonical_path'], false, (string) $resolved['root_key']);
        if (is_wp_error($confirmed)) {
            return;
        }

        wp_delete_file((string) $confirmed['canonical_path']);
    }

    /**
     * Copy a prepared JPEG into the direct public Meta-safe upload store.
     *
     * @param string $source_path Source image path.
     * @param string $context     Network subdirectory.
     * @param string $prefix      Filename prefix.
     * @return array<string,mixed>|WP_Error
     */
    private static function copy_image_to_meta_static_store($source_path, $context, $prefix) {
        $source = self::resolve_plugin_media_file($source_path, true, 'media');
        if (is_wp_error($source)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }
        $source_path = (string) $source['canonical_path'];

        $source_meta = self::inspect_image_file($source_path);
        if (is_wp_error($source_meta)) {
            return $source_meta;
        }

        if (strtolower((string) ($source_meta['mime'] ?? '')) !== 'image/jpeg' || !self::file_starts_with_jpeg_magic($source_path)) {
            return new WP_Error('invalid_image_file', 'The Meta-safe media store only accepts true JPEG files.');
        }

        $store = self::ensure_meta_static_store($context);
        if (is_wp_error($store)) {
            return $store;
        }

        $prefix = sanitize_file_name((string) $prefix);
        if ($prefix === '') {
            $prefix = 'csp-meta';
        }

        $filename = $prefix . '-' . gmdate('Ymd-His') . '-' . wp_generate_password(8, false, false) . '.jpg';
        $target = self::resolve_plugin_media_target(trailingslashit((string) $store['dir']) . $filename, 'meta');
        if (is_wp_error($target)) {
            return new WP_Error('upload_failed', 'Could not prepare the Meta-safe image destination.');
        }
        $target_path = (string) $target['canonical_path'];

        $filesystem = self::get_wp_filesystem();
        if (!$filesystem || !$filesystem->copy($source_path, $target_path, true, defined('FS_CHMOD_FILE') ? FS_CHMOD_FILE : 0644)) {
            return new WP_Error('upload_failed', 'Could not copy the JPEG into the Meta-safe media store.');
        }

        self::chmod_file($target_path);

        $target_file = self::resolve_plugin_media_file($target_path, true, 'meta');
        if (is_wp_error($target_file)) {
            self::delete_file($target_path);
            return new WP_Error('invalid_image_file', 'The Meta-safe JPEG file could not be validated.');
        }
        $target_path = (string) $target_file['canonical_path'];

        $meta = self::inspect_image_file($target_path);
        if (is_wp_error($meta)) {
            self::delete_file($target_path);
            return $meta;
        }

        if (strtolower((string) ($meta['mime'] ?? '')) !== 'image/jpeg' || !self::file_starts_with_jpeg_magic($target_path)) {
            self::delete_file($target_path);
            return new WP_Error('invalid_image_file', 'The Meta-safe JPEG file does not start with valid JPEG bytes.');
        }

        $url = self::build_public_url_from_upload_path($target_path);
        if (is_wp_error($url)) {
            self::delete_file($target_path);
            return $url;
        }

        return [
            'path' => $target_path,
            'url'  => (string) $url,
            'meta' => $meta,
        ];
    }

    /**
     * Create a static-looking .jpg URL that is served dynamically from WordPress.
     *
     * This avoids wp-json URLs while giving hosts/CDNs less opportunity to rewrite
     * the original static JPEG before Instagram fetches it.
     *
     * @param string $path    Absolute local JPEG path.
     * @param string $context Network subdirectory.
     * @param string $prefix  Filename prefix.
     * @param int    $ttl     URL lifetime in seconds.
     * @return string|WP_Error
     */
    public static function create_meta_static_proxy_url($path, $context, $prefix, $ttl = 21600) {
        $resolved = self::resolve_plugin_media_file($path, true, 'meta');
        if (is_wp_error($resolved)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }
        $path = (string) $resolved['canonical_path'];

        $meta = self::inspect_image_file($path);
        if (is_wp_error($meta)) {
            return $meta;
        }

        if (strtolower((string) ($meta['mime'] ?? '')) !== 'image/jpeg' || !self::file_starts_with_jpeg_magic($path)) {
            return new WP_Error('invalid_image_file', 'The static-looking Meta URL only accepts true JPEG files.');
        }

        $context = sanitize_key((string) $context);
        if (!in_array($context, ['instagram', 'threads'], true)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not belong to a supported Meta media context.');
        }
        if (strpos((string) $resolved['relative_path'], $context . '/') !== 0) {
            return new WP_Error('invalid_image_file', 'The generated image file does not belong to the requested Meta media context.');
        }

        $store = self::ensure_meta_static_store($context);
        if (is_wp_error($store)) {
            return $store;
        }

        $uploads = wp_upload_dir();
        if (!empty($uploads['error'])) {
            return new WP_Error('upload_failed', 'Could not access the uploads directory: ' . $uploads['error']);
        }

        $prefix = sanitize_file_name((string) $prefix);
        if ($prefix === '') {
            $prefix = 'csp-meta';
        }

        $filename = $prefix . '-passthrough-' . gmdate('Ymd-His') . '-' . wp_generate_password(8, false, false) . '.jpg';
        $relative = $context . '/' . $filename;
        $key = self::get_meta_static_proxy_transient_key($relative);

        set_transient($key, [
            'path'          => $path,
            'root_key'      => 'meta',
            'relative_path' => (string) $resolved['relative_path'],
            'mime'          => 'image/jpeg',
            'created'       => time(),
            'expires'       => time() + max(300, (int) $ttl),
            'filesize'      => (int) filesize($path),
        ], max(300, (int) $ttl));

        $url = trailingslashit((string) $uploads['baseurl']) . 'calliope-social-publisher-meta/' . rawurlencode($context) . '/' . rawurlencode($filename);
        return esc_url_raw(self::normalize_public_url($url));
    }

    /**
     * Ensure the dedicated Meta media directory and protective files exist.
     *
     * @param string $context Network subdirectory.
     * @return array{dir:string}|WP_Error
     */
    private static function ensure_meta_static_store($context) {
        $uploads = wp_upload_dir();
        if (!empty($uploads['error'])) {
            return new WP_Error('upload_failed', 'Could not access the uploads directory: ' . $uploads['error']);
        }

        $context = sanitize_key((string) $context);
        if ($context === '') {
            $context = 'shared';
        }

        $base_dir = trailingslashit((string) $uploads['basedir']) . 'calliope-social-publisher-meta/';
        $dir = trailingslashit($base_dir) . $context . '/';

        if (!wp_mkdir_p($dir)) {
            return new WP_Error('upload_failed', 'Could not create the Meta-safe uploads directory.');
        }

        $resolved_base = self::resolve_plugin_media_directory($base_dir, 'meta');
        $resolved_dir = self::resolve_plugin_media_directory($dir, 'meta');
        if (is_wp_error($resolved_base) || is_wp_error($resolved_dir)) {
            return new WP_Error('upload_failed', 'Could not validate the Meta-safe uploads directory.');
        }

        $htaccess = "# Social Publisher Meta-safe media store.\n"
            . "<IfModule mod_mime.c>\n"
            . "AddType image/jpeg .jpg .jpeg\n"
            . "</IfModule>\n\n"
            . "<IfModule mod_headers.c>\n"
            . "Header set Cache-Control \"public, max-age=3600, no-transform\"\n"
            . "Header set Accept-Ranges \"bytes\"\n"
            . "Header set Content-Disposition \"inline\"\n"
            . "Header set X-Content-Type-Options \"nosniff\"\n"
            . "Header unset Content-Encoding\n"
            . "</IfModule>\n\n"
            . "<IfModule mod_rewrite.c>\n"
            . "RewriteEngine On\n"
            . "RewriteCond %{REQUEST_FILENAME} !-f\n"
            . "RewriteRule ^(instagram|threads)/([^/]+\\.jpg)$ /index.php?calliope_sp_meta_media=$1/$2 [L,QSA]\n"
            . "</IfModule>\n";

        foreach ([(string) $resolved_base['canonical_path'], (string) $resolved_dir['canonical_path']] as $target_dir) {
            if (!self::write_text_file_if_changed(trailingslashit($target_dir) . 'index.html', '')) {
                return new WP_Error('upload_failed', 'Could not write the Meta-safe uploads index file.');
            }

            if (!self::write_text_file_if_changed(trailingslashit($target_dir) . '.htaccess', $htaccess)) {
                return new WP_Error('upload_failed', 'Could not write the Meta-safe uploads .htaccess file.');
            }
        }

        return ['dir' => (string) $resolved_dir['canonical_path']];
    }

    /**
     * Write a small text file only when its contents differ.
     *
     * @param string $path     File path.
     * @param string $contents File contents.
     * @return bool
     */
    private static function write_text_file_if_changed($path, $contents) {
        $contents = (string) $contents;
        $target = self::resolve_plugin_media_target($path, 'meta');
        if (is_wp_error($target)) {
            return false;
        }
        $path = (string) $target['canonical_path'];

        $filesystem = self::get_wp_filesystem();
        if (!$filesystem) {
            return false;
        }

        if ($filesystem->exists($path) && $filesystem->is_file($path)) {
            $existing = self::resolve_plugin_media_file($path, true, 'meta');
            if (is_wp_error($existing)) {
                return false;
            }
            $path = (string) $existing['canonical_path'];
            $current = $filesystem->get_contents($path);
            if ($current === $contents) {
                return true;
            }
        }

        if (!$filesystem->put_contents($path, $contents, defined('FS_CHMOD_FILE') ? FS_CHMOD_FILE : 0644)) {
            return false;
        }

        $written = self::resolve_plugin_media_file($path, true, 'meta');
        if (is_wp_error($written)) {
            return false;
        }

        self::chmod_file((string) $written['canonical_path']);
        return true;
    }

    /**
     * Register a signed public media endpoint used by Meta APIs when static uploads
     * are blocked by hotlink protection, CDN rules, or MIME/header quirks.
     *
     * The route is intentionally public because Meta must fetch it without a logged-in
     * WordPress session. Access is limited by an unguessable token stored in a short
     * lived transient and by strict uploads-directory path validation.
     *
     * @return void
     */
    public static function register_rest_routes() {
        register_rest_route('calliope-social-publisher/v1', '/media/(?P<token>[A-Za-z0-9]+)/(?P<filename>[^/]+)', [
            'methods'             => ['GET', 'HEAD'],
            'permission_callback' => '__return_true',
            'callback'            => [__CLASS__, 'serve_signed_public_media'],
        ]);

        register_rest_route('calliope-social-publisher/v1', '/media/(?P<token>[A-Za-z0-9]+)', [
            'methods'             => ['GET', 'HEAD'],
            'permission_callback' => '__return_true',
            'callback'            => [__CLASS__, 'serve_signed_public_media'],
        ]);
    }

    /**
     * Create a temporary signed URL that streams the already generated local image
     * through WordPress with explicit image headers.
     *
     * @param string $path Absolute local path.
     * @param string $mime Image MIME type.
     * @param int    $ttl  URL lifetime in seconds.
     * @return string|WP_Error
     */
    public static function create_signed_public_media_url($path, $mime, $ttl = 21600) {
        $resolved = self::resolve_plugin_media_file($path, true);
        if (is_wp_error($resolved)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }
        $path = (string) $resolved['canonical_path'];

        $meta = self::inspect_image_file($path);
        if (is_wp_error($meta) || !self::is_supported_mime((string) ($meta['mime'] ?? ''))) {
            return new WP_Error('invalid_image_file', 'The generated file is not a valid supported image.');
        }
        $mime = strtolower((string) $meta['mime']);

        $token = wp_generate_password(40, false, false);
        set_transient('calliope_sp_public_media_' . $token, [
            'path'          => $path,
            'root_key'      => (string) $resolved['root_key'],
            'relative_path' => (string) $resolved['relative_path'],
            'mime'          => $mime,
            'created'       => time(),
            'expires'       => time() + max(300, (int) $ttl),
            'filesize'      => (int) filesize($path),
        ], max(300, (int) $ttl));

        $url = rest_url('calliope-social-publisher/v1/media/' . rawurlencode($token) . '/' . rawurlencode(basename($path)));
        return esc_url_raw(self::normalize_public_url($url));
    }

    /**
     * Serve a signed temporary media file to Meta crawlers.
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_Error|void
     */
    public static function serve_signed_public_media($request) {
        $token = sanitize_text_field((string) $request->get_param('token'));
        if ($token === '') {
            return new WP_Error('calliope_sp_media_not_found', 'Media not found.', ['status' => 404]);
        }

        $data = get_transient('calliope_sp_public_media_' . $token);
        if (!is_array($data) || (!empty($data['expires']) && (int) $data['expires'] < time())) {
            return new WP_Error('calliope_sp_media_not_found', 'Media not found or expired.', ['status' => 404]);
        }

        $resolved = self::resolve_plugin_media_file((string) ($data['path'] ?? ''), true);
        if (is_wp_error($resolved)) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.', ['status' => 404]);
        }

        if (!empty($data['root_key']) && (string) $data['root_key'] !== (string) $resolved['root_key']) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.', ['status' => 404]);
        }
        if (!empty($data['relative_path']) && (string) $data['relative_path'] !== (string) $resolved['relative_path']) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.', ['status' => 404]);
        }

        $path = (string) $resolved['canonical_path'];
        $requested_filename = (string) $request->get_param('filename');
        if ($requested_filename !== '' && $requested_filename !== basename($path)) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.', ['status' => 404]);
        }

        $meta = self::inspect_image_file($path);
        if (is_wp_error($meta) || !self::is_supported_mime((string) ($meta['mime'] ?? ''))) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.', ['status' => 404]);
        }

        // Revalidate immediately before reading to reduce the filesystem race window.
        $confirmed = self::resolve_plugin_media_file($path, true, (string) $resolved['root_key']);
        if (is_wp_error($confirmed)) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.', ['status' => 404]);
        }
        $path = (string) $confirmed['canonical_path'];
        $mime = strtolower((string) $meta['mime']);
        $filesize = (int) filesize($path);

        status_header(200);
        header('Content-Type: ' . $mime);
        header('Content-Length: ' . $filesize);
        header('Cache-Control: public, max-age=21600, no-transform');
        header('Accept-Ranges: bytes');
        header('Access-Control-Allow-Origin: *');
        header('X-Content-Type-Options: nosniff');
        header('X-Robots-Tag: noindex, nofollow', true);
        header('Content-Disposition: inline; filename="' . basename($path) . '"');

        if (self::get_request_method() !== 'HEAD') {
            self::output_file_contents($path);
        }

        exit;
    }

    /**
     * Serve static-looking Meta media URLs rewritten from the uploads subfolder.
     *
     * @return void
     */
    public static function maybe_serve_meta_static_proxy() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public media URL uses an unguessable transient-backed filename and strict path validation.
        $relative = isset($_GET['calliope_sp_meta_media']) ? sanitize_text_field(wp_unslash((string) $_GET['calliope_sp_meta_media'])) : '';
        if ($relative === '') {
            return;
        }

        $relative = str_replace('\\', '/', $relative);
        $relative = ltrim($relative, '/');
        if (!preg_match('#^(instagram|threads)/[A-Za-z0-9._-]+\.jpg$#', $relative, $matches)) {
            status_header(404);
            exit;
        }
        $requested_context = (string) $matches[1];

        $data = get_transient(self::get_meta_static_proxy_transient_key($relative));
        if (!is_array($data) || (!empty($data['expires']) && (int) $data['expires'] < time())) {
            status_header(404);
            exit;
        }

        $resolved = self::resolve_plugin_media_file((string) ($data['path'] ?? ''), true, 'meta');
        if (is_wp_error($resolved)) {
            status_header(404);
            exit;
        }
        if (!empty($data['root_key']) && (string) $data['root_key'] !== 'meta') {
            status_header(404);
            exit;
        }
        if (!empty($data['relative_path']) && (string) $data['relative_path'] !== (string) $resolved['relative_path']) {
            status_header(404);
            exit;
        }
        if (strpos((string) $resolved['relative_path'], $requested_context . '/') !== 0) {
            status_header(404);
            exit;
        }

        $path = (string) $resolved['canonical_path'];
        $meta = self::inspect_image_file($path);
        if (is_wp_error($meta) || strtolower((string) ($meta['mime'] ?? '')) !== 'image/jpeg' || !self::file_starts_with_jpeg_magic($path)) {
            status_header(404);
            exit;
        }

        // Revalidate immediately before reading to reduce the filesystem race window.
        $confirmed = self::resolve_plugin_media_file($path, true, 'meta');
        if (is_wp_error($confirmed)) {
            status_header(404);
            exit;
        }
        $path = (string) $confirmed['canonical_path'];
        $filesize = (int) filesize($path);

        nocache_headers();
        header_remove('Expires');
        header_remove('Pragma');
        header_remove('Content-Encoding');
        status_header(200);
        header('Content-Type: image/jpeg');
        header('Content-Length: ' . $filesize);
        header('Cache-Control: public, max-age=3600, no-transform');
        header('Accept-Ranges: bytes');
        header('Access-Control-Allow-Origin: *');
        header('X-Content-Type-Options: nosniff');
        header('X-Robots-Tag: noindex, nofollow', true);
        header('Content-Disposition: inline; filename="' . basename($relative) . '"');

        if (self::get_request_method() !== 'HEAD') {
            self::output_file_contents($path);
        }

        exit;
    }

    /**
     * Get the transient key for a static-looking Meta media URL.
     *
     * @param string $relative Relative network/filename path.
     * @return string
     */
    private static function get_meta_static_proxy_transient_key($relative) {
        return 'calliope_sp_meta_static_proxy_' . sha1((string) $relative);
    }

    /**
     * Reject stream wrappers and malformed filesystem input before any filesystem call.
     *
     * @param mixed $path Raw path.
     * @return bool
     */
    private static function is_safe_filesystem_path_input($path) {
        if (!is_string($path) || $path === '' || strpos($path, "\0") !== false) {
            return false;
        }

        return !preg_match('#^[a-z][a-z0-9+.-]*://#i', trim($path));
    }

    /**
     * Normalize path separators without treating textual normalization as canonicalization.
     *
     * @param string $path Filesystem path.
     * @return string
     */
    private static function normalize_filesystem_path($path) {
        return rtrim(wp_normalize_path(str_replace('\\', '/', (string) $path)), '/');
    }

    /**
     * Compare canonical path strings with a real directory boundary.
     * The optional override makes Windows semantics testable on non-Windows hosts.
     *
     * @param string    $path       Canonical candidate path.
     * @param string    $root       Canonical root path.
     * @param bool      $allow_root Whether equality with the root is valid.
     * @param bool|null $windows    Optional Windows comparison override.
     * @return bool
     */
    private static function canonical_path_is_inside_root($path, $root, $allow_root = false, $windows = null) {
        $path = self::normalize_filesystem_path($path);
        $root = self::normalize_filesystem_path($root);
        if ($path === '' || $root === '') {
            return false;
        }

        if ($windows === null) {
            $windows = PHP_OS_FAMILY === 'Windows'
                || preg_match('#^[A-Za-z]:/#', $path)
                || preg_match('#^[A-Za-z]:/#', $root)
                || strpos($path, '//') === 0
                || strpos($root, '//') === 0;
        }

        if ($windows) {
            $path = strtolower($path);
            $root = strtolower($root);
        }

        if ($allow_root && $path === $root) {
            return true;
        }

        return strpos($path, $root . '/') === 0;
    }

    /**
     * Return logical and canonical information for the two plugin-owned media roots.
     * The general uploads root may itself be a hosting symlink, but plugin subroots may not.
     *
     * @return array<string,array<string,string>>|WP_Error
     */
    private static function get_plugin_media_roots() {
        $uploads = wp_upload_dir();
        if (!empty($uploads['error']) || !self::is_safe_filesystem_path_input((string) ($uploads['basedir'] ?? ''))) {
            return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is unavailable.');
        }

        $logical_uploads = self::normalize_filesystem_path((string) $uploads['basedir']);
        $canonical_uploads_raw = realpath($logical_uploads);
        if ($canonical_uploads_raw === false || !is_dir($canonical_uploads_raw)) {
            return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is unavailable.');
        }
        $canonical_uploads = self::normalize_filesystem_path($canonical_uploads_raw);

        $definitions = [
            'media' => 'calliope-social-publisher',
            'meta'  => 'calliope-social-publisher-meta',
        ];
        $roots = [];

        foreach ($definitions as $root_key => $folder) {
            $logical_root = $logical_uploads . '/' . $folder;
            if (!file_exists($logical_root)) {
                continue;
            }
            if (is_link($logical_root) || !is_dir($logical_root)) {
                return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
            }

            $canonical_root_raw = realpath($logical_root);
            if ($canonical_root_raw === false) {
                return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
            }
            $canonical_root = self::normalize_filesystem_path($canonical_root_raw);
            if (!self::canonical_path_is_inside_root($canonical_root, $canonical_uploads, false)) {
                return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
            }

            $roots[$root_key] = [
                'root_key'       => $root_key,
                'logical_root'   => $logical_root,
                'canonical_root' => $canonical_root,
            ];
        }

        return $roots;
    }

    /**
     * Reject symlink components below a plugin media root. The uploads basedir itself
     * may be a valid hosting symlink and is deliberately outside this walk.
     *
     * @param string $logical_root Logical plugin root.
     * @param string $relative     Relative path below that root.
     * @return bool
     */
    private static function path_has_symlink_below_root($logical_root, $relative) {
        $current = self::normalize_filesystem_path($logical_root);
        if ($current === '' || is_link($current)) {
            return true;
        }

        $relative = trim(str_replace('\\', '/', (string) $relative), '/');
        if ($relative === '') {
            return false;
        }

        foreach (explode('/', $relative) as $segment) {
            if ($segment === '') {
                return true;
            }
            if ($segment === '.') {
                continue;
            }
            if ($segment === '..') {
                return true;
            }
            $current .= '/' . $segment;
            if (is_link($current)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Inspect the caller-supplied path itself so a symlink alias cannot disappear
     * after realpath() canonicalizes it to an otherwise safe in-root location.
     *
     * @param string               $input Normalized caller path.
     * @param array<string,string> $root  Root metadata.
     * @return bool
     */
    private static function input_path_has_symlink_below_root($input, array $root) {
        $input = self::normalize_filesystem_path($input);
        foreach (['logical_root', 'canonical_root'] as $root_field) {
            $candidate_root = self::normalize_filesystem_path((string) ($root[$root_field] ?? ''));
            if ($candidate_root === '' || !self::canonical_path_is_inside_root($input, $candidate_root, true)) {
                continue;
            }

            $relative = ltrim(substr($input, strlen($candidate_root)), '/');
            return self::path_has_symlink_below_root($candidate_root, $relative);
        }

        return false;
    }

    /**
     * Resolve an existing directory inside one of the plugin-owned media roots.
     *
     * @param string $path          Existing directory path.
     * @param string $expected_root Optional root key: media or meta.
     * @return array<string,string>|WP_Error
     */
    private static function resolve_plugin_media_directory($path, $expected_root = '') {
        if (!self::is_safe_filesystem_path_input($path)) {
            return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
        }

        $input = self::normalize_filesystem_path($path);
        if (is_link($input)) {
            return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
        }

        $canonical_raw = realpath($input);
        if ($canonical_raw === false || !is_dir($canonical_raw)) {
            return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
        }
        $canonical = self::normalize_filesystem_path($canonical_raw);
        $roots = self::get_plugin_media_roots();
        if (is_wp_error($roots)) {
            return $roots;
        }

        foreach ($roots as $root_key => $root) {
            if ($expected_root !== '' && $root_key !== $expected_root) {
                continue;
            }
            if (!self::canonical_path_is_inside_root($canonical, $root['canonical_root'], true)) {
                continue;
            }

            $relative = ltrim(substr($canonical, strlen($root['canonical_root'])), '/');
            if (self::input_path_has_symlink_below_root($input, $root)
                || self::path_has_symlink_below_root($root['logical_root'], $relative)) {
                return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
            }

            return [
                'canonical_path' => $canonical,
                'logical_path'   => $relative === '' ? $root['logical_root'] : $root['logical_root'] . '/' . $relative,
                'root_key'       => $root_key,
                'canonical_root' => $root['canonical_root'],
                'logical_root'   => $root['logical_root'],
                'relative_path'  => $relative,
            ];
        }

        return new WP_Error('calliope_sp_invalid_media', 'The plugin media directory is invalid.');
    }

    /**
     * Resolve and contain an existing regular file inside a plugin-owned media root.
     *
     * @param string $path             Existing file path.
     * @param bool   $require_readable Require read access.
     * @param string $expected_root    Optional root key: media or meta.
     * @return array<string,string>|WP_Error
     */
    private static function resolve_plugin_media_file($path, $require_readable = true, $expected_root = '') {
        if (!self::is_safe_filesystem_path_input($path)) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $input = self::normalize_filesystem_path($path);
        if (is_link($input)) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $canonical_raw = realpath($input);
        if ($canonical_raw === false || !is_file($canonical_raw)) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.');
        }
        if ($require_readable && !is_readable($canonical_raw)) {
            return new WP_Error('calliope_sp_media_not_found', 'Media file not found.');
        }
        $canonical = self::normalize_filesystem_path($canonical_raw);

        $roots = self::get_plugin_media_roots();
        if (is_wp_error($roots)) {
            return $roots;
        }

        foreach ($roots as $root_key => $root) {
            if ($expected_root !== '' && $root_key !== $expected_root) {
                continue;
            }
            if (!self::canonical_path_is_inside_root($canonical, $root['canonical_root'], false)) {
                continue;
            }

            $relative = ltrim(substr($canonical, strlen($root['canonical_root'])), '/');
            if ($relative === ''
                || self::input_path_has_symlink_below_root($input, $root)
                || self::path_has_symlink_below_root($root['logical_root'], $relative)) {
                return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
            }

            return [
                'canonical_path' => $canonical,
                'logical_path'   => $root['logical_root'] . '/' . $relative,
                'root_key'       => $root_key,
                'canonical_root' => $root['canonical_root'],
                'logical_root'   => $root['logical_root'],
                'relative_path'  => $relative,
            ];
        }

        return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
    }

    /**
     * Validate a future write target whose parent directory already exists safely.
     *
     * @param string $path          Future or existing file path.
     * @param string $expected_root Optional root key: media or meta.
     * @return array<string,string>|WP_Error
     */
    private static function resolve_plugin_media_target($path, $expected_root = '') {
        if (!self::is_safe_filesystem_path_input($path)) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $input = self::normalize_filesystem_path($path);
        $basename = basename($input);
        if ($basename === '' || $basename === '.' || $basename === '..' || strpos($basename, '..') !== false || strpos($basename, "\0") !== false) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $safe_basename = $basename === '.htaccess' ? '.htaccess' : sanitize_file_name($basename);
        if ($safe_basename === '' || strpos($safe_basename, '/') !== false || strpos($safe_basename, '\\') !== false || strpos($safe_basename, '..') !== false) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $parent = self::resolve_plugin_media_directory(dirname($input), $expected_root);
        if (is_wp_error($parent)) {
            return $parent;
        }

        $target = self::normalize_filesystem_path((string) $parent['canonical_path'] . '/' . $safe_basename);
        if (file_exists($target) || is_link($target)) {
            if (is_link($target) || is_dir($target)) {
                return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
            }
            return self::resolve_plugin_media_file($target, false, (string) $parent['root_key']);
        }

        if (!self::canonical_path_is_inside_root($target, (string) $parent['canonical_root'], false)) {
            return new WP_Error('calliope_sp_invalid_media', 'Invalid media file.');
        }

        $relative = ltrim(substr($target, strlen((string) $parent['canonical_root'])), '/');
        return [
            'canonical_path' => $target,
            'logical_path'   => (string) $parent['logical_root'] . '/' . $relative,
            'root_key'       => (string) $parent['root_key'],
            'canonical_root' => (string) $parent['canonical_root'],
            'logical_root'   => (string) $parent['logical_root'],
            'relative_path'  => $relative,
        ];
    }

    /**
     * Encode a validated relative filesystem path as URL path segments.
     *
     * @param string $relative Validated path relative to a plugin media root.
     * @return string|WP_Error
     */
    private static function encode_safe_relative_url_path($relative) {
        $relative = trim(str_replace('\\', '/', (string) $relative), '/');
        if ($relative === '') {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }

        $encoded = [];
        foreach (explode('/', $relative) as $segment) {
            if ($segment === '' || $segment === '.' || $segment === '..' || strpos($segment, '\\') !== false || strpos($segment, '/') !== false) {
                return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
            }
            $encoded[] = rawurlencode($segment);
        }

        return implode('/', $encoded);
    }

    /**
     * Compatibility predicate backed by canonical path resolution.
     *
     * @param string $path Absolute path.
     * @return bool
     */
    private static function is_path_inside_plugin_uploads($path) {
        return !is_wp_error(self::resolve_plugin_media_file($path, false));
    }

    /**
     * @param string $b64 Base64 payload.
     * @return array<string,mixed>|WP_Error
     */
    private static function decode_base64_image($b64) {
        if (!is_string($b64) || trim($b64) === '') {
            return new WP_Error('no_image', 'No image data was provided.');
        }

        $mime = '';
        $raw = trim($b64);

        if (preg_match('#^data:([^;]+);base64,(.*)$#s', $raw, $matches)) {
            $mime = strtolower(trim((string) $matches[1]));
            $raw = (string) $matches[2];
        }

        $raw = preg_replace('#\s+#', '', $raw);
        $binary = base64_decode((string) $raw, true);
        if ($binary === false || $binary === '') {
            return new WP_Error('bad_base64', 'The image data could not be decoded.');
        }

        $info = function_exists('getimagesizefromstring') ? @getimagesizefromstring($binary) : false;
        if (!is_array($info) || empty($info[0]) || empty($info[1])) {
            return new WP_Error('invalid_image_file', 'The decoded file is not a valid image.');
        }

        $detected_mime = !empty($info['mime']) ? strtolower((string) $info['mime']) : $mime;
        if (!self::is_supported_mime($detected_mime)) {
            return new WP_Error('invalid_image_type', 'The image type is not supported for publishing.');
        }

        return [
            'binary'    => $binary,
            'mime'      => $detected_mime,
            'extension' => self::guess_extension_from_mime($detected_mime),
            'width'     => (int) $info[0],
            'height'    => (int) $info[1],
        ];
    }

    /**
     * Convert an existing file to the preferred output MIME.
     *
     * @param string $path           Absolute file path.
     * @param string $preferred_mime Preferred MIME.
     * @return array{path:string,mime:string}|WP_Error
     */
    private static function convert_image_file($path, $preferred_mime) {
        if (!function_exists('wp_get_image_editor')) {
            return new WP_Error('image_editor_missing', 'WordPress image editing is not available on this site.');
        }

        $source = self::resolve_plugin_media_file($path, true, 'media');
        if (is_wp_error($source)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }
        $path = (string) $source['canonical_path'];

        $preferred_mime = strtolower((string) $preferred_mime);
        if (!self::is_supported_mime($preferred_mime)) {
            return new WP_Error('invalid_image_type', 'The requested output image type is not supported.');
        }

        $editor = wp_get_image_editor($path);
        if (is_wp_error($editor)) {
            return $editor;
        }

        if (method_exists($editor, 'set_quality')) {
            $editor->set_quality(90);
        }

        $proposed_path = preg_replace('/\.[^.]+$/', '', $path) . '.' . self::guess_extension_from_mime($preferred_mime);
        $target = self::resolve_plugin_media_target($proposed_path, 'media');
        if (is_wp_error($target)) {
            return new WP_Error('image_save_failed', 'The converted image file could not be saved.');
        }
        $target_path = (string) $target['canonical_path'];

        $saved = $editor->save($target_path, $preferred_mime);
        if (is_wp_error($saved) || empty($saved['path'])) {
            return is_wp_error($saved)
                ? $saved
                : new WP_Error('image_save_failed', 'The converted image file could not be saved.');
        }

        $saved_file = self::resolve_plugin_media_file((string) $saved['path'], true, 'media');
        if (is_wp_error($saved_file)) {
            return new WP_Error('image_save_failed', 'The converted image file could not be validated.');
        }

        $saved_path = (string) $saved_file['canonical_path'];
        if ($saved_path !== $path) {
            self::delete_file($path);
        }

        return [
            'path' => $saved_path,
            'mime' => !empty($saved['mime-type']) ? strtolower((string) $saved['mime-type']) : $preferred_mime,
        ];
    }

    /**
     * Re-encode a file through WordPress' image editor to get a fresh baseline JPEG.
     *
     * @param string $path           Absolute file path.
     * @param string $preferred_mime Preferred MIME.
     * @return array{path:string,mime:string}|WP_Error
     */
    private static function reencode_image_file($path, $preferred_mime) {
        if (!function_exists('wp_get_image_editor')) {
            return new WP_Error('image_editor_missing', 'WordPress image editing is not available on this site.');
        }

        $source = self::resolve_plugin_media_file($path, true, 'media');
        if (is_wp_error($source)) {
            return new WP_Error('invalid_image_file', 'The generated image file does not exist.');
        }
        $path = (string) $source['canonical_path'];

        $preferred_mime = strtolower((string) $preferred_mime);
        if ($preferred_mime !== 'image/jpeg') {
            return self::convert_image_file($path, $preferred_mime);
        }

        $editor = wp_get_image_editor($path);
        if (is_wp_error($editor)) {
            return $editor;
        }

        if (method_exists($editor, 'set_quality')) {
            $editor->set_quality(90);
        }

        $proposed_path = preg_replace('/\.[^.]+$/', '', $path) . '-jpeg.jpg';
        $target = self::resolve_plugin_media_target($proposed_path, 'media');
        if (is_wp_error($target)) {
            return new WP_Error('image_save_failed', 'The re-encoded JPEG file could not be saved.');
        }
        $target_path = (string) $target['canonical_path'];

        $saved = $editor->save($target_path, 'image/jpeg');
        if (is_wp_error($saved) || empty($saved['path'])) {
            return is_wp_error($saved)
                ? $saved
                : new WP_Error('image_save_failed', 'The re-encoded JPEG file could not be saved.');
        }

        $saved_file = self::resolve_plugin_media_file((string) $saved['path'], true, 'media');
        if (is_wp_error($saved_file)) {
            return new WP_Error('image_save_failed', 'The re-encoded JPEG file could not be validated.');
        }

        $saved_path = (string) $saved_file['canonical_path'];
        if ($saved_path !== $path) {
            self::delete_file($path);
        }

        return [
            'path' => $saved_path,
            'mime' => !empty($saved['mime-type']) ? strtolower((string) $saved['mime-type']) : 'image/jpeg',
        ];
    }

    /**
     * @param string $mime MIME type.
     * @return bool
     */
    private static function is_supported_mime($mime) {
        return in_array(strtolower((string) $mime), [
            'image/jpeg',
            'image/png',
            'image/webp',
            'image/gif',
        ], true);
    }

    /**
     * Normalize a Content-Type header into only the MIME value.
     *
     * @param string $content_type Raw Content-Type header.
     * @return string
     */
    private static function normalize_content_type($content_type) {
        $content_type = strtolower(trim((string) $content_type));
        if ($content_type === '') {
            return '';
        }

        $semi_pos = strpos($content_type, ';');
        if ($semi_pos !== false) {
            $content_type = substr($content_type, 0, $semi_pos);
        }

        return trim($content_type);
    }

    /**
     * Check whether a public response has an image Content-Type and, if requested,
     * the exact MIME required by Meta.
     *
     * @param string $content_type Raw Content-Type header.
     * @param string $required_mime Optional exact MIME, for example image/jpeg.
     * @return bool
     */
    private static function is_allowed_public_content_type($content_type, $required_mime = '') {
        $normalized = self::normalize_content_type($content_type);
        $required_mime = self::normalize_content_type($required_mime);

        if ($normalized === '') {
            return false;
        }

        if ($required_mime !== '') {
            return $normalized === $required_mime;
        }

        return strpos($normalized, 'image/') === 0;
    }

    /**
     * Detect the actual image MIME from response bytes, useful when a CDN changes
     * a .jpg URL into WebP while keeping a JPEG-looking filename.
     *
     * @param string $binary Response body bytes.
     * @return string
     */
    private static function detect_image_mime_from_binary($binary) {
        $binary = (string) $binary;
        if ($binary === '') {
            return '';
        }

        if (strncmp($binary, "\xFF\xD8\xFF", 3) === 0) {
            return 'image/jpeg';
        }
        if (strncmp($binary, "\x89PNG\r\n\x1a\n", 8) === 0) {
            return 'image/png';
        }
        if (strncmp($binary, 'GIF87a', 6) === 0 || strncmp($binary, 'GIF89a', 6) === 0) {
            return 'image/gif';
        }
        if (substr($binary, 0, 4) === 'RIFF' && substr($binary, 8, 4) === 'WEBP') {
            return 'image/webp';
        }

        return '';
    }

    /**
     * Read a prefix of a file as lowercase hex.
     *
     * @param string $path  Absolute path.
     * @param int    $bytes Number of bytes.
     * @return string
     */
    private static function read_file_prefix_hex($path, $bytes = 12) {
        $path = (string) $path;
        if ($path === '' || !is_readable($path)) {
            return '';
        }

        $contents = self::get_file_contents($path);
        if ($contents === false) {
            return '';
        }

        $prefix = substr((string) $contents, 0, max(1, (int) $bytes));

        return bin2hex($prefix);
    }

    /**
     * Check the local file magic bytes for JPEG.
     *
     * @param string $path Absolute path.
     * @return bool
     */
    private static function file_starts_with_jpeg_magic($path) {
        $path = (string) $path;
        if ($path === '' || !is_readable($path)) {
            return false;
        }

        $contents = self::get_file_contents($path);
        if ($contents === false) {
            return false;
        }

        $prefix = substr((string) $contents, 0, 3);

        return strncmp($prefix, "\xFF\xD8\xFF", 3) === 0;
    }

    /**
     * @return WP_Filesystem_Base|null
     */
    private static function get_wp_filesystem() {
        global $wp_filesystem;

        if (!function_exists('WP_Filesystem')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        if (!WP_Filesystem()) {
            return null;
        }

        return $wp_filesystem;
    }

    /**
     * Apply WordPress' file permissions when the helper is available.
     *
     * Some legitimate execution paths, including WP-CLI media operations, may
     * not load the optional chmod helper even though WP_Filesystem is available.
     *
     * @param string $path Absolute file path.
     * @return void
     */
    private static function chmod_file($path) {
        $filesystem = self::get_wp_filesystem();
        if ($filesystem && method_exists($filesystem, 'chmod')) {
            $filesystem->chmod((string) $path, defined('FS_CHMOD_FILE') ? FS_CHMOD_FILE : 0644);
            return;
        }

        if (function_exists('wp_chmod_file')) {
            wp_chmod_file($path);
        }
    }

    /**
     * @param string $path Absolute file path.
     * @return string|false
     */
    private static function get_file_contents($path) {
        $resolved = self::resolve_plugin_media_file($path, true);
        if (is_wp_error($resolved)) {
            return false;
        }

        $filesystem = self::get_wp_filesystem();
        if (!$filesystem) {
            return false;
        }

        return $filesystem->get_contents((string) $resolved['canonical_path']);
    }

    /**
     * @param string $path Absolute file path.
     * @return void
     */
    private static function output_file_contents($path) {
        $resolved = self::resolve_plugin_media_file($path, true);
        if (is_wp_error($resolved)) {
            status_header(404);
            return;
        }

        $contents = self::get_file_contents((string) $resolved['canonical_path']);
        if ($contents === false) {
            status_header(404);
            return;
        }

        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary image response is validated before output and sent with image headers.
        echo $contents;
    }

    /**
     * @return string
     */
    private static function get_request_method() {
        $method = isset($_SERVER['REQUEST_METHOD'])
            ? sanitize_key(wp_unslash((string) $_SERVER['REQUEST_METHOD']))
            : 'get';

        return strtoupper($method);
    }

    /**
     * @param string $content_type HTTP content type.
     * @return bool
     */
    private static function is_image_content_type($content_type) {
        $content_type = self::normalize_content_type($content_type);
        if ($content_type === '') {
            return false;
        }

        return strpos($content_type, 'image/') === 0;
    }

    /**
     * @param string $mime MIME type.
     * @return string
     */
    private static function guess_extension_from_mime($mime) {
        $map = [
            'image/jpeg' => 'jpg',
            'image/png'  => 'png',
            'image/webp' => 'webp',
            'image/gif'  => 'gif',
        ];

        $mime = strtolower((string) $mime);
        return $map[$mime] ?? 'jpg';
    }
}
