<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * @package Wisewand
 * @since 1.0.0
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Media;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use WP_Error;
use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;
use Wisewand\Utils\Content_Cleaner;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- error_log() is only used within WP_DEBUG conditionals for debugging

class Media_Manager {

    private $options;
    private $logger;

    public function __construct(Options $options, Logger $logger) {
        $this->options = $options;
        $this->logger = $logger;
    }

    /**
     * Download media with optional AI-generated metadata
     *
     * @param string $url Media URL
     * @param int $post_id Post ID to attach to
     * @param string $description Description/title
     * @param array $metadata Optional AI metadata (alt, title, caption, description)
     * @return array|WP_Error Result with attachment_id or error
     */
    public function download_media($url, $post_id = 0, $description = '', $metadata = []) {
        $media_settings = $this->options->get_media_settings();

        // Validate URL
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            return new WP_Error(
                'wisewand_invalid_url',
                __('Invalid media URL provided.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        // SECURITY: Smart SSRF protection with automatic trust detection
        // Block internal IPs ONLY for untrusted contexts (webhooks, APIs, CRON)
        // Allow internal IPs for trusted contexts (admin actions, development)
        $should_check_ssrf = $this->should_apply_ssrf_protection($media_settings);

        if ($should_check_ssrf) {
            $ssrf_check = $this->is_url_safe_to_download($url);
            if (is_wp_error($ssrf_check)) {
                return $ssrf_check;
            }
        }

        // Check allowed domains if configured
        if (!$this->is_domain_allowed($url, $media_settings)) {
            return new WP_Error(
                'wisewand_domain_not_allowed',
                __('Media domain not in allowed list.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        $this->logger->debug('Starting media download', [
            'url' => $url,
            'post_id' => $post_id,
            'has_ai_metadata' => !empty($metadata),
        ]);

        // Download the file
        $download_result = $this->download_file($url, $media_settings);
        if (is_wp_error($download_result)) {
            return $download_result;
        }

        // Import into WordPress media library with metadata
        $attachment_id = $this->import_to_media_library(
            $download_result['file_path'],
            $download_result['filename'],
            $download_result['mime_type'],
            $post_id,
            $description,
            $metadata
        );

        if (is_wp_error($attachment_id)) {
            // Clean up downloaded file
            wp_delete_file($download_result['file_path']);
            return $attachment_id;
        }

        $this->logger->info('Media downloaded successfully', [
            'url' => $url,
            'attachment_id' => $attachment_id,
            'post_id' => $post_id,
        ]);

        return [
            'attachment_id' => $attachment_id,
            'url' => wp_get_attachment_url($attachment_id),
            'file_path' => get_attached_file($attachment_id),
            'mime_type' => $download_result['mime_type'],
        ];
    }

    /**
     * Download and attach featured image with optional AI-generated metadata
     *
     * @param string $url Image URL
     * @param int $post_id Post ID
     * @param array $metadata Optional AI-generated metadata (alt, title, caption, description)
     * @return array|WP_Error Result with attachment_id or error
     */
    public function download_featured_image($url, $post_id, $metadata = []) {
        $description = !empty($metadata['title']) ? $metadata['title'] : 'Featured image from Wisewand';
        $result = $this->download_media($url, $post_id, $description, $metadata);

        if (is_wp_error($result)) {
            return $result;
        }

        // Set as featured image
        $set_result = set_post_thumbnail($post_id, $result['attachment_id']);

        if (!$set_result) {
            return new WP_Error(
                'wisewand_featured_image_failed',
                __('Failed to set featured image.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        return $result;
    }

    /**
     * Download gallery images with optional AI-generated metadata
     *
     * @param array $image_urls Array of image URLs
     * @param int $post_id Post ID
     * @param array $gallery_metadata Optional array of metadata arrays (one per image)
     * @return array Results with successful downloads and errors
     */
    public function download_gallery_images($image_urls, $post_id, $gallery_metadata = []) {
        $results = [];
        $errors = [];

        foreach ($image_urls as $index => $url) {
            // Get metadata for this specific image if provided
            $image_metadata = $gallery_metadata[$index] ?? [];

            $description = !empty($image_metadata['title']) ? $image_metadata['title'] : "Gallery image #{$index} from Wisewand";
            $result = $this->download_media($url, $post_id, $description, $image_metadata);

            if (is_wp_error($result)) {
                $errors[] = [
                    'url' => $url,
                    'error' => $result->get_error_message(),
                ];
                continue;
            }

            $results[] = $result;
        }

        // Store gallery attachment IDs in post meta
        if (!empty($results)) {
            $attachment_ids = wp_list_pluck($results, 'attachment_id');
            update_post_meta($post_id, '_wisewand_gallery_images', $attachment_ids);
        }

        return [
            'successful' => $results,
            'errors' => $errors,
        ];
    }

    public function download_audio($url, $post_id) {
        $result = $this->download_media($url, $post_id, 'Audio from Wisewand');

        if (is_wp_error($result)) {
            return $result;
        }

        $local_audio_url = wp_get_attachment_url($result['attachment_id']);

        // Store audio URL in post meta
        update_post_meta($post_id, '_wisewand_audio_url', $local_audio_url);
        update_post_meta($post_id, '_wisewand_audio_id', $result['attachment_id']);

        // Note: Audio player will be added to content by Import_Enhancer

        return $result;
    }

    public function add_audio_player_to_content( $post_id, $audio_url, $attachment_id = null ) {
        $post = get_post($post_id);
        if (!$post) {
            return false;
        }

        // Create audio player HTML via Content_Cleaner (single source of truth)
        $audio_html = Content_Cleaner::generate_audio_player( $audio_url, $attachment_id );

        // Check if audio player already exists in content
        if (strpos($post->post_content, 'wisewand-audio-player') !== false) {
            $this->logger->debug('Audio player already exists in content', ['post_id' => $post_id]);
            return true;
        }

        // Add audio player at the beginning of content
        $updated_content = $audio_html . "\n\n" . $post->post_content;

        // Temporarily allow <source> tags during wp_update_post
        // WordPress's default kses filtering strips <source> tags from audio players
        add_filter( 'wp_kses_allowed_html', array( 'Wisewand\\Utils\\Content_Cleaner', 'allow_audio_kses' ), 10, 2 );

        $update_result = wp_update_post([
            'ID' => $post_id,
            'post_content' => $updated_content,
        ]);

        // Remove filter immediately after update to maintain security
        remove_filter( 'wp_kses_allowed_html', array( 'Wisewand\\Utils\\Content_Cleaner', 'allow_audio_kses' ), 10 );

        if (is_wp_error($update_result)) {
            $this->logger->error('Failed to add audio player to content', [
                'post_id' => $post_id,
                'error' => $update_result->get_error_message(),
            ]);
            return false;
        }

        $this->logger->info('Audio player added to post content', [
            'post_id' => $post_id,
            'audio_url' => $audio_url,
            'attachment_id' => $attachment_id,
        ]);

        return true;
    }

    /**
     * Process inline images with optional AI metadata generation
     *
     * @param string $content HTML content with images
     * @param int $post_id Post ID
     * @param string $article_title Article title for AI context
     * @param string $article_content Full content for AI context
     * @param array $output Article output data for fallback
     * @return string Updated content with local image URLs
     */
    public function process_inline_images($content, $post_id, $article_title = '', $article_content = '', $output = []) {
        // Always process inline images for Wisewand content

        $this->logger->info('Starting comprehensive image processing', [
            'post_id' => $post_id,
            'content_length' => strlen($content),
            'has_ai_context' => !empty($article_title),
        ]);

        // Check if AI metadata generation is enabled
        $plugin = \Wisewand\Core\Plugin::instance();
        $options = $plugin->get_options();
        $generate_ai_metadata = $options && $options->get('wisewand_generate_ai_image_metadata', false) && \Wisewand\Utils\OpenAI_Client::is_configured();

        // Find all image URLs in content (multiple patterns to catch everything)
        $image_patterns = [
            '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', // Standard img tags
            '/https?:\/\/[^\s<>"]+\.(?:jpg|jpeg|png|gif|webp|svg)/i', // Direct image URLs
        ];

        $all_image_urls = [];

        foreach ($image_patterns as $pattern) {
            preg_match_all($pattern, $content, $matches, PREG_SET_ORDER);
            foreach ($matches as $match) {
                $image_url = $match[1] ?? $match[0];
                if (!in_array($image_url, $all_image_urls)) {
                    $all_image_urls[] = $image_url;
                }
            }
        }

        $this->logger->debug('Found images in content', [
            'post_id' => $post_id,
            'total_images' => count($all_image_urls),
            'image_urls' => $all_image_urls,
        ]);

        $inline_image_index = 1;

        foreach ($all_image_urls as $image_url) {
            // Skip if already a local URL
            if (strpos($image_url, home_url()) === 0) {
                continue;
            }

            // Skip if it's a data URL, relative URL, or invalid
            if (strpos($image_url, 'data:') === 0 ||
                (strpos($image_url, '//') !== 0 && strpos($image_url, 'http') !== 0) ||
                !filter_var($image_url, FILTER_VALIDATE_URL)) {
                continue;
            }

            $this->logger->debug('Processing external image', [
                'original_url' => $image_url,
                'post_id' => $post_id,
                'inline_index' => $inline_image_index,
            ]);

            // Generate AI metadata for inline image if enabled
            $inline_metadata = [];
            if ($generate_ai_metadata && !empty($article_title)) {
                $inline_metadata = $this->generate_inline_image_metadata(
                    $article_title,
                    $article_content,
                    $image_url,
                    $inline_image_index,
                    $output
                );
            }

            // Download and replace
            $description = !empty($inline_metadata['title']) ? $inline_metadata['title'] : 'Inline image from Wisewand';
            $download_result = $this->download_media($image_url, $post_id, $description, $inline_metadata);

            if (!is_wp_error($download_result)) {
                $new_url = wp_get_attachment_url($download_result['attachment_id']);

                // Replace ALL occurrences of this URL in the content
                $content = str_replace($image_url, $new_url, $content);

                // Also update any srcset attributes that might reference this image
                $content = $this->update_srcset_references($content, $image_url, $new_url);

                $this->logger->info('Successfully replaced inline image', [
                    'original_url' => $image_url,
                    'new_url' => $new_url,
                    'attachment_id' => $download_result['attachment_id'],
                    'post_id' => $post_id,
                    'inline_index' => $inline_image_index,
                ]);
            } else {
                $this->logger->warning('Failed to download inline image', [
                    'url' => $image_url,
                    'post_id' => $post_id,
                    'error' => $download_result->get_error_message(),
                ]);
            }

            // Increment index for next inline image
            $inline_image_index++;
        }

        return $content;
    }

    public function process_inline_audio($content, $post_id) {
        // Find all audio tags and audio URLs in content
        $patterns = [
            '/<audio[^>]+src=["\']([^"\']+)["\'][^>]*>/i',
            '/<source[^>]+src=["\']([^"\']+)["\'][^>]*>/i',
            '/https?:\/\/[^\s<>"]+\.(?:mp3|wav|ogg|m4a)/i'
        ];

        foreach ($patterns as $pattern) {
            preg_match_all($pattern, $content, $matches, PREG_SET_ORDER);

            foreach ($matches as $match) {
                $audio_url = $match[1] ?? $match[0];

                // Skip if already a local URL
                if (strpos($audio_url, home_url()) === 0) {
                    continue;
                }

                $this->logger->debug('Processing inline audio', [
                    'original_url' => $audio_url,
                    'post_id' => $post_id,
                ]);

                // Download audio file
                $download_result = $this->download_media($audio_url, $post_id, 'Inline audio from Wisewand');

                if (!is_wp_error($download_result)) {
                    $new_url = wp_get_attachment_url($download_result['attachment_id']);

                    // Replace the URL in content
                    $content = str_replace($audio_url, $new_url, $content);

                    $this->logger->info('Successfully replaced inline audio', [
                        'original_url' => $audio_url,
                        'new_url' => $new_url,
                        'attachment_id' => $download_result['attachment_id'],
                        'post_id' => $post_id,
                    ]);
                } else {
                    $this->logger->warning('Failed to download inline audio', [
                        'url' => $audio_url,
                        'post_id' => $post_id,
                        'error' => $download_result->get_error_message(),
                    ]);
                }
            }
        }

        return $content;
    }

    private function update_srcset_references($content, $old_url, $new_url) {
        // Update srcset attributes that might reference the old URL
        return preg_replace_callback('/srcset=["\']([^"\']+)["\']/i', function($matches) use ($old_url, $new_url) {
            $srcset = $matches[1];
            $updated_srcset = str_replace($old_url, $new_url, $srcset);
            return 'srcset="' . $updated_srcset . '"';
        }, $content);
    }

    private function download_file($url, array $media_settings) {
        // Parse URL to get filename
        $parsed_url = wp_parse_url($url);
        $path_info = pathinfo($parsed_url['path']);
        $filename = $path_info['basename'];

        // Clean Wisewand prefix from filename (e.g., "cd6epcbt_image.webp" → "image.webp")
        $filename = $this->clean_wisewand_filename($filename);

        // Sanitize filename
        $filename = sanitize_file_name($filename);
        if (empty($filename)) {
            $filename = 'wisewand-media-' . time();
        }

        // Create temporary file
        $temp_file = wp_tempnam($filename);

        // Set up HTTP request arguments with STREAMING enabled
        // This prevents loading entire file into memory (critical for large files)
        $args = [
            'timeout' => $media_settings['timeout'],
            'stream' => true,  // Stream to file instead of loading into memory
            'filename' => $temp_file,  // Write directly to this file
            'user-agent' => 'Wisewand-plugin/' . WISEWAND_PLUGIN_VERSION,
            'reject_unsafe_urls' => true,  // SSRF protection: reject local/private IPs
            'headers' => [
                'Accept' => 'image/*,audio/*,*/*',
            ],
        ];

        // Download file with streaming (memory-efficient)
        $response = wp_remote_get($url, $args);

        if (is_wp_error($response)) {
            wp_delete_file($temp_file);
            return new WP_Error(
                'wisewand_download_failed',
                sprintf(
                    /* translators: %s: Error message from download attempt */
                    __('Failed to download media: %s', 'wisewand-ai-content-writer-seo-content-generator'),
                    $response->get_error_message())
            );
        }

        $response_code = wp_remote_retrieve_response_code($response);
        if ($response_code !== 200) {
            wp_delete_file($temp_file);
            return new WP_Error(
                'wisewand_download_http_error',
                sprintf(
                    /* translators: %d: HTTP response code (e.g., 404, 500) */
                    __('HTTP error downloading media: %d', 'wisewand-ai-content-writer-seo-content-generator'),
                    $response_code)
            );
        }

        // Check file size AFTER streaming (file is already on disk, no memory spike)
        if (!file_exists($temp_file)) {
            return new WP_Error(
                'wisewand_stream_failed',
                __('Streaming download failed - temp file not created.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        $body_size = filesize($temp_file);

        // Validate file size
        if ($body_size > $media_settings['max_size']) {
            wp_delete_file($temp_file);
            return new WP_Error(
                'wisewand_file_too_large',
                sprintf(
                    /* translators: 1: Actual file size, 2: Maximum allowed file size */
                    __('File size (%1$s) exceeds maximum allowed size (%2$s).', 'wisewand-ai-content-writer-seo-content-generator'),
                    size_format($body_size),
                    size_format($media_settings['max_size'])
                )
            );
        }

        if ($body_size === 0) {
            wp_delete_file($temp_file);
            return new WP_Error(
                'wisewand_empty_file',
                __('Downloaded file is empty.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        // Validate Content-Type header for security
        $content_type = wp_remote_retrieve_header($response, 'content-type');
        if ($content_type) {
            // Extract base MIME type (remove charset, etc.)
            $content_type_parts = explode(';', $content_type);
            $base_mime_type = trim($content_type_parts[0]);

            // Whitelist of allowed MIME types for security
            $allowed_content_types = [
                'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
                'audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/ogg', 'audio/mp4',
            ];

            if (!in_array($base_mime_type, $allowed_content_types, true)) {
                wp_delete_file($temp_file);
                $this->logger->warning('Rejected file with invalid Content-Type', [
                    'url' => $url,
                    'content_type' => $content_type,
                ]);
                return new WP_Error(
                    'wisewand_invalid_content_type',
                    sprintf(
                        /* translators: %s: Content-Type header value received */
                        __('Invalid Content-Type received: %s', 'wisewand-ai-content-writer-seo-content-generator'),
                        $content_type)
                );
            }
        }

        // File is now on disk (streamed), ready for processing
        $this->logger->debug('Media file streamed successfully', [
            'url' => $url,
            'size' => size_format($body_size),
            'temp_file' => $temp_file,
        ]);

        // Detect MIME type
        $mime_type = $this->get_mime_type($temp_file, $filename);

        // Validate MIME type
        if (!$this->is_mime_type_allowed($mime_type, $media_settings)) {
            wp_delete_file($temp_file);
            return new WP_Error(
                'wisewand_invalid_mime_type',
                sprintf(
                    /* translators: %s: MIME type of the file (e.g., 'image/png', 'application/pdf') */
                    __('File type not allowed: %s', 'wisewand-ai-content-writer-seo-content-generator'),
                    $mime_type)
            );
        }

        // Add proper extension if missing
        $expected_extension = $this->get_extension_for_mime_type($mime_type);
        if ($expected_extension && !$this->ends_with(strtolower($filename), '.' . $expected_extension)) {
            $filename .= '.' . $expected_extension;
        }

        return [
            'file_path' => $temp_file,
            'filename' => $filename,
            'mime_type' => $mime_type,
            'size' => $body_size,
        ];
    }

    /**
     * Import file to WordPress media library with optional AI metadata
     *
     * @param string $file_path Temporary file path
     * @param string $filename Original filename
     * @param string $mime_type MIME type
     * @param int $post_id Post ID to attach to
     * @param string $description Default description
     * @param array $metadata Optional AI-generated metadata (alt, title, caption, description)
     * @return int|WP_Error Attachment ID or error
     */
    private function import_to_media_library($file_path, $filename, $mime_type, $post_id = 0, $description = '', $metadata = []) {
        // Load core file handling functions if not already loaded
        if (!function_exists('wp_handle_sideload')) {
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        }
        if (!function_exists('media_handle_sideload')) {
            require_once(ABSPATH . 'wp-admin/includes/media.php');
        }
        if (!function_exists('wp_generate_attachment_metadata')) {
            require_once(ABSPATH . 'wp-admin/includes/image.php');
        }

        // Prepare file array for wp_handle_sideload
        $file_array = [
            'name' => $filename,
            'type' => $mime_type,
            'tmp_name' => $file_path,
            'error' => 0,
            'size' => filesize($file_path),
        ];

        // Import file
        $upload_result = wp_handle_sideload($file_array, [
            'test_form' => false,
            'test_size' => true,
            'test_upload' => true,
        ]);

        if (isset($upload_result['error'])) {
            return new WP_Error(
                'wisewand_sideload_failed',
                $upload_result['error']
            );
        }

        // Create attachment post with AI metadata if available
        $attachment_data = [
            'post_mime_type' => $mime_type,
            'post_title' => !empty($metadata['title']) ? sanitize_text_field($metadata['title']) : sanitize_text_field($description ?: pathinfo($filename, PATHINFO_FILENAME)),
            'post_content' => !empty($metadata['description']) ? sanitize_textarea_field($metadata['description']) : '',
            'post_excerpt' => !empty($metadata['caption']) ? sanitize_textarea_field($metadata['caption']) : '',
            'post_status' => 'inherit',
            'post_parent' => $post_id,
        ];

        $attachment_id = wp_insert_attachment($attachment_data, $upload_result['file'], $post_id);

        if (is_wp_error($attachment_id)) {
            wp_delete_file($upload_result['file']);
            return $attachment_id;
        }

        // Generate WordPress attachment metadata (dimensions, sizes, etc.)
        $attachment_metadata = wp_generate_attachment_metadata($attachment_id, $upload_result['file']);
        wp_update_attachment_metadata($attachment_id, $attachment_metadata);

        // Set AI-generated alt text (critical for accessibility & SEO)
        if (!empty($metadata['alt'])) {
            update_post_meta($attachment_id, '_wp_attachment_image_alt', sanitize_text_field($metadata['alt']));

            if (WP_DEBUG) {
                error_log('MEDIA: Set AI-generated alt text: "' . $metadata['alt'] . '"');
            }
        }

        // Mark as Wisewand import
        update_post_meta($attachment_id, '_wisewand_import', true);
        update_post_meta($attachment_id, '_wisewand_import_date', current_time('mysql'));

        // Store metadata source for tracking
        if (!empty($metadata)) {
            update_post_meta($attachment_id, '_wisewand_ai_metadata', true);
        }

        return $attachment_id;
    }

    private function get_mime_type($file_path, $filename) {
        // Try to get MIME type from file content
        if (function_exists('finfo_file')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mime_type = finfo_file($finfo, $file_path);
            finfo_close($finfo);

            if ($mime_type) {
                return $mime_type;
            }
        }

        // Fallback to WordPress function
        $wp_check = wp_check_filetype($filename);
        if ($wp_check['type']) {
            return $wp_check['type'];
        }

        return 'application/octet-stream';
    }

    private function is_mime_type_allowed($mime_type, array $media_settings) {
        return in_array($mime_type, $media_settings['allowed_mime_types'], true);
    }

    /**
     * SECURITY: Smart SSRF protection - automatically determine if protection should apply
     *
     * Protection is DISABLED (allow internal IPs) for:
     * - Development environments (WP_DEBUG, WP_ENVIRONMENT_TYPE = local)
     * - Admin users in WordPress dashboard (trusted context)
     * - Manual admin actions (not automated)
     *
     * Protection is ENABLED (block internal IPs) for:
     * - Webhook calls from external APIs (untrusted source)
     * - CRON automated imports (no user supervision)
     * - REST API calls from external sources
     * - Any context where admin explicitly disabled it
     *
     * @param array $media_settings Media configuration settings
     * @return bool True if SSRF protection should be applied, false to allow internal IPs
     */
    private function should_apply_ssrf_protection($media_settings) {
        // 1. Check explicit admin configuration first
        if (isset($media_settings['allow_internal_urls']) && $media_settings['allow_internal_urls'] === true) {
            $this->logger->debug('SSRF Protection: Disabled by admin configuration', [
                'context' => 'explicit_config'
            ]);
            return false; // Admin explicitly allowed internal URLs
        }

        // 2. Auto-detect development environment
        if ($this->is_development_environment()) {
            $this->logger->debug('SSRF Protection: Disabled - development environment detected', [
                'wp_debug' => defined('WP_DEBUG') && WP_DEBUG,
                'wp_environment' => defined('WP_ENVIRONMENT_TYPE') ? WP_ENVIRONMENT_TYPE : 'not set',
            ]);
            return false; // Allow internal IPs in development
        }

        // 3. Detect trusted admin context (user manually triggered action)
        if ($this->is_trusted_admin_context()) {
            $this->logger->info('SSRF Protection: Relaxed - trusted admin action in dashboard', [
                'user_id' => get_current_user_id(),
                'user_can_manage' => current_user_can('manage_options'),
                'is_admin' => is_admin(),
            ]);
            return false; // Allow internal IPs for admin actions
        }

        // 4. Default: Apply SSRF protection for untrusted contexts
        $context = $this->detect_request_context();
        $this->logger->info('SSRF Protection: Enabled - untrusted context detected', [
            'context' => $context,
            'note' => 'Blocking internal/private IPs to prevent SSRF attacks'
        ]);
        return true; // Block internal IPs
    }

    /**
     * Detect if running in development environment
     *
     * @return bool True if development environment
     */
    private function is_development_environment() {
        // Check WP_ENVIRONMENT_TYPE (WordPress 5.5+)
        if (defined('WP_ENVIRONMENT_TYPE')) {
            $env = WP_ENVIRONMENT_TYPE;
            if (in_array($env, ['local', 'development', 'dev', 'staging'], true)) {
                return true;
            }
        }

        // Check WP_DEBUG (legacy detection)
        if (defined('WP_DEBUG') && WP_DEBUG === true) {
            // Additional check: WP_DEBUG_DISPLAY to avoid false positives
            // Some production sites have WP_DEBUG=true but WP_DEBUG_DISPLAY=false
            if (defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY === false) {
                return false; // Production with debug logging
            }
            return true; // Likely development
        }

        // Check for localhost/127.0.0.1 in site URL
        $site_url = get_site_url();
        if (preg_match('/^https?:\/\/(localhost|127\.0\.0\.1|::1)/i', $site_url)) {
            return true;
        }

        return false;
    }

    /**
     * Detect if request is from trusted admin context
     *
     * Trusted contexts:
     * - Admin user in WordPress dashboard (is_admin)
     * - AJAX request from admin with proper authentication
     * - User has manage_options capability
     *
     * @return bool True if trusted admin context
     */
    private function is_trusted_admin_context() {
        // Must be authenticated user
        if (!is_user_logged_in()) {
            return false;
        }

        // Must be in admin area OR admin AJAX
        if (!is_admin() && !wp_doing_ajax()) {
            return false;
        }

        // For AJAX, verify it's from admin area
        if (wp_doing_ajax()) {
            // Check referer to ensure AJAX comes from admin
            $referer = wp_get_referer();
            if (!$referer || strpos($referer, admin_url()) !== 0) {
                return false; // AJAX not from admin area
            }
        }

        // Must have edit_posts capability at minimum
        // (manage_options would be too restrictive for editors)
        if (!current_user_can('edit_posts')) {
            return false;
        }

        return true; // Trusted admin context
    }

    /**
     * Detect the request context for logging
     *
     * @return string Context description
     */
    private function detect_request_context() {
        if (wp_doing_cron()) {
            return 'cron_automation';
        }

        if (defined('REST_REQUEST') && REST_REQUEST) {
            return 'rest_api';
        }

        if (wp_doing_ajax()) {
            return 'ajax_request';
        }

        if (is_admin()) {
            return 'admin_area';
        }

        return 'frontend';
    }

    /**
     * SECURITY: Check if URL is safe to download (SSRF protection)
     * Blocks requests to internal/private IP addresses to prevent Server-Side Request Forgery
     *
     * @param string $url The URL to validate
     * @return true|WP_Error True if safe, WP_Error if dangerous
     */
    private function is_url_safe_to_download($url) {
        $parsed_url = wp_parse_url($url);

        if (!isset($parsed_url['host'])) {
            return new WP_Error(
                'wisewand_invalid_url_host',
                __('Invalid URL: Missing host.', 'wisewand-ai-content-writer-seo-content-generator')
            );
        }

        $host = $parsed_url['host'];

        // Resolve hostname to IP address(es)
        // Use @ to suppress warnings if DNS lookup fails
        $ip_addresses = @gethostbynamel($host);

        // If DNS resolution fails, try single IP lookup as fallback
        if ($ip_addresses === false) {
            $single_ip = @gethostbyname($host);
            if ($single_ip !== $host) {
                $ip_addresses = [$single_ip];
            } else {
                // DNS resolution completely failed - treat as potentially dangerous
                $this->logger->warning('SSRF Protection: DNS resolution failed', [
                    'host' => $host,
                    'url' => $url,
                ]);
                return new WP_Error(
                    'wisewand_dns_resolution_failed',
                    __('Unable to resolve hostname. URL blocked for security.', 'wisewand-ai-content-writer-seo-content-generator')
                );
            }
        }

        // Check each resolved IP address
        foreach ($ip_addresses as $ip) {
            if (!$this->is_ip_address_safe($ip)) {
                $this->logger->warning('SSRF Protection: Blocked dangerous IP', [
                    'host' => $host,
                    'resolved_ip' => $ip,
                    'url' => $url,
                ]);

                return new WP_Error(
                    'wisewand_ssrf_blocked',
                    sprintf(
                        /* translators: %s: IP address that was blocked (e.g., '127.0.0.1', '192.168.1.1') */
                        __('Security: URL resolves to internal/private IP address (%s). Download blocked.', 'wisewand-ai-content-writer-seo-content-generator'),
                        $ip
                    )
                );
            }
        }

        return true;
    }

    /**
     * SECURITY: Check if an IP address is safe to connect to
     * Blocks internal, private, and reserved IP ranges
     *
     * @param string $ip IP address to check
     * @return bool True if safe, false if dangerous
     */
    private function is_ip_address_safe($ip) {
        // Validate IP format first
        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            return false;
        }

        // Block private and reserved IP ranges using PHP's built-in filter
        // This blocks:
        // - 10.0.0.0/8 (Private)
        // - 172.16.0.0/12 (Private)
        // - 192.168.0.0/16 (Private)
        // - 127.0.0.0/8 (Loopback)
        // - 169.254.0.0/16 (Link-local)
        // - 224.0.0.0/4 (Multicast)
        // - 240.0.0.0/4 (Reserved)
        // - IPv6 equivalents (::1, fc00::/7, fe80::/10, etc.)
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
            return false;
        }

        // Additional check: Block localhost explicitly (defense in depth)
        if ( '127.0.0.1' === $ip || '::1' === $ip || 'localhost' === $ip ) {
            return false;
        }

        // Additional check: Block 0.0.0.0 (bind all interfaces)
        if ( '0.0.0.0' === $ip || '::' === $ip ) {
            return false;
        }

        return true;
    }

    private function is_domain_allowed($url, array $media_settings) {
        $allowed_domains = trim($media_settings['allowed_domains']);

        // If no domain restrictions, allow all (but log domain for monitoring)
        $parsed_url = wp_parse_url($url);
        if (!isset($parsed_url['host'])) {
            return false;
        }

        $domain = strtolower($parsed_url['host']);

        // Auto-allow known Wisewand domains
        $wisewand_domains = [
            'cdn.wisewand.ai',
            'gohmqpvtliqcvxerzehl.supabase.co', // Wisewand's Supabase storage
            'storage.googleapis.com',
            'amazonaws.com',
        ];

        foreach ($wisewand_domains as $wisewand_domain) {
            if ($domain === $wisewand_domain || $this->ends_with($domain, '.' . $wisewand_domain)) {
                $this->logger->debug('Auto-allowed Wisewand domain', [
                    'domain' => $domain,
                    'url' => $url,
                ]);
                return true;
            }
        }

        // If no restrictions configured, allow all
        if (empty($allowed_domains)) {
            return true;
        }

        $allowed_list = array_map('trim', array_map('strtolower', explode("\n", $allowed_domains)));

        foreach ($allowed_list as $allowed_domain) {
            if (empty($allowed_domain)) {
                continue;
            }

            // Check for wildcard subdomains
            if ($this->starts_with($allowed_domain, '*.')) {
                $wildcard_domain = substr($allowed_domain, 2);
                if ($domain === $wildcard_domain || $this->ends_with($domain, '.' . $wildcard_domain)) {
                    return true;
                }
            } else {
                // Exact domain match
                if ($domain === $allowed_domain) {
                    return true;
                }
            }
        }

        $this->logger->warning('Domain not allowed for media download', [
            'domain' => $domain,
            'url' => $url,
            'allowed_domains' => $allowed_domains,
        ]);

        return false;
    }

    private function get_extension_for_mime_type($mime_type) {
        $mime_to_ext = [
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/gif' => 'gif',
            'image/webp' => 'webp',
            'audio/mpeg' => 'mp3',
            'audio/wav' => 'wav',
            'audio/ogg' => 'ogg',
            'audio/mp4' => 'm4a',
        ];

        return $mime_to_ext[$mime_type] ?? null;
    }

    public function cleanup_wisewand_media($post_id) {
        // Get all attachments for this post that were imported by Wisewand
        $attachments = get_posts([
            'post_type' => 'attachment',
            'post_parent' => $post_id,
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- meta_query required to identify Wisewand-imported attachments for cleanup
            'meta_query' => [
                [
                    'key' => '_wisewand_import',
                    'value' => true,
                ],
            ],
            'numberposts' => -1,
        ]);

        foreach ($attachments as $attachment) {
            wp_delete_attachment($attachment->ID, true);
        }

        $this->logger->debug('Cleaned up Wisewand media', [
            'post_id' => $post_id,
            'attachments_deleted' => count($attachments),
        ]);
    }

    // Helper methods for PHP compatibility
    private function ends_with($haystack, $needle) {
        $length = strlen($needle);
        if ($length == 0) {
            return true;
        }
        return (substr($haystack, -$length) === $needle);
    }

    private function starts_with($haystack, $needle) {
        return strpos($haystack, $needle) === 0;
    }

    /**
     * Clean Wisewand filename prefixes (e.g., "cd6epcbt_image.webp" → "image.webp")
     */
    private function clean_wisewand_filename($filename) {
        // Pattern: 8 alphanumeric characters followed by underscore
        if (preg_match('/^[a-zA-Z0-9]{8}_(.+)$/', $filename, $matches)) {
            $cleaned_filename = $matches[1];

            $this->logger->debug('Cleaned Wisewand filename', [
                'original' => $filename,
                'cleaned' => $cleaned_filename,
            ]);

            return $cleaned_filename;
        }

        return $filename;
    }

    /**
     * Generate AI metadata for inline images with fallback
     *
     * @param string $article_title Article title
     * @param string $article_content Full content
     * @param string $image_url Image URL
     * @param int $index Image index
     * @param array $output Article output data for fallback
     * @return array Metadata with alt, title, caption, description
     */
    private function generate_inline_image_metadata($article_title, $article_content, $image_url, $index, $output) {
        // Try OpenAI generation
        if (!class_exists('Wisewand\\Utils\\OpenAI_Client')) {
            require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/OpenAI_Client.php';
        }

        $api_key = \Wisewand\Utils\OpenAI_Client::get_stored_api_key();

        if (empty($api_key)) {
            return $this->get_fallback_metadata($article_title, $output);
        }

        $openai_client = new \Wisewand\Utils\OpenAI_Client($api_key, $this->logger);

        $metadata = $openai_client->generate_image_metadata(
            $article_title,
            $article_content,
            $image_url,
            'inline',
            $index
        );

        if (is_wp_error($metadata)) {
            if (WP_DEBUG) {
                error_log('AI_METADATA: Inline image #' . $index . ' generation failed, using fallback');
            }
            return $this->get_fallback_metadata($article_title, $output);
        }

        return $metadata;
    }

    /**
     * Get fallback metadata when OpenAI fails
     *
     * @param string $article_title Article title
     * @param array $output Article output data
     * @return array Fallback metadata
     */
    private function get_fallback_metadata($article_title, $output) {
        $meta_title = $output['meta_title'] ?? $article_title;

        return [
            'alt' => $meta_title,
            'title' => $meta_title,
            'caption' => '',
            'description' => '',
        ];
    }

}