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

class Cloud_Uploads {
    
    private $s3_client;
    private $settings;
    private $memory_limit;
    
    public function __construct($s3_client, $settings) {
        $this->s3_client = $s3_client;
        $this->settings = $settings;
        $this->memory_limit = $this->get_memory_limit();
        
        $this->setup_hooks();
    }
    
    private function setup_hooks() {
        // Sync on upload - use later hooks to ensure file is available
        add_action('add_attachment', array($this, 'sync_attachment'), 20, 1);
        add_action('edit_attachment', array($this, 'sync_attachment'), 20, 1);
        
        // Hook into AJAX upload completion
        add_filter('wp_ajax_upload-attachment', array($this, 'handle_ajax_upload'), 5, 1);
        
        // Handle image sizes - sync all generated sizes
        add_filter('wp_generate_attachment_metadata', array($this, 'sync_attachment_metadata'), 20, 2);
        
        // Sync on delete
        add_action('delete_attachment', array($this, 'delete_attachment'), 10, 1);
        
        // Bulk actions
        add_action('admin_init', array($this, 'handle_bulk_actions'));
        
        // Optimize server resources for image processing
        add_filter('big_image_size_threshold', array($this, 'increase_big_image_threshold'), 10, 4);
        add_filter('wp_editor_set_quality', array($this, 'adjust_image_quality'), 10, 2);
        add_filter('jpeg_quality', array($this, 'adjust_jpeg_quality'), 10, 2);
    }
    
    /**
     * Get attachment ID from file path - Enhanced for new uploads
     */
    public function get_attachment_id_from_file($file_path) {
        global $wpdb;
        
        // Make sure we have a valid file path
        if (empty($file_path) || !file_exists($file_path)) {
            error_log('Cloud Uploads: Invalid file path provided: ' . $file_path);
            return 0;
        }
        
        $upload_dir = wp_upload_dir();
        $base_dir = $upload_dir['basedir'];
        
        // Convert absolute path to relative path within uploads directory
        $relative_path = str_replace($base_dir . '/', '', $file_path);
        
        // Method 1: Search by _wp_attached_file meta key (exact match)
        $attachment_id = $this->find_attachment_by_attached_file($relative_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        // Method 2: Search by GUID (URL in database)
        $attachment_id = $this->find_attachment_by_guid($relative_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        // Method 3: Search for the file without the upload directory base
        $attachment_id = $this->find_attachment_by_filename($file_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        // Method 4: Search by file name in posts table
        $attachment_id = $this->find_attachment_by_post_title($file_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        // Method 5: For new uploads - check recent attachments without relying on file time
        $attachment_id = $this->find_recent_attachment($file_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        // Method 6: Last resort - scan all recent attachments and match by content
        $attachment_id = $this->find_attachment_by_content_match($file_path);
        if ($attachment_id) {
            return $attachment_id;
        }
        
        error_log('Cloud Uploads: Could not find attachment ID for file: ' . $file_path);
        return 0;
    }
    
    /**
     * Method 1: Find attachment by exact _wp_attached_file match
     */
    private function find_attachment_by_attached_file($relative_path) {
        global $wpdb;
        
        $query = $wpdb->prepare(
            "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
            $relative_path
        );
        
        $attachment_id = $wpdb->get_var($query);
        
        if ($attachment_id) {
            error_log('Cloud Uploads: Found attachment by attached file: ' . $attachment_id);
            return (int) $attachment_id;
        }
        
        return 0;
    }
    
    /**
     * Method 2: Find attachment by GUID match
     */
    private function find_attachment_by_guid($relative_path) {
        global $wpdb;
        
        $upload_dir = wp_upload_dir();
        $file_url = $upload_dir['baseurl'] . '/' . $relative_path;
        
        $query = $wpdb->prepare(
            "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND guid = %s",
            $file_url
        );
        
        $attachment_id = $wpdb->get_var($query);
        
        if ($attachment_id) {
            error_log('Cloud Uploads: Found attachment by GUID: ' . $attachment_id);
            return (int) $attachment_id;
        }
        
        // Try partial match for cases where URL might be slightly different
        $query = $wpdb->prepare(
            "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND guid LIKE %s",
            '%' . $wpdb->esc_like(basename($relative_path)) . '%'
        );
        
        $attachment_id = $wpdb->get_var($query);
        
        if ($attachment_id) {
            error_log('Cloud Uploads: Found attachment by GUID partial match: ' . $attachment_id);
            return (int) $attachment_id;
        }
        
        return 0;
    }
    
    /**
     * Method 3: Find attachment by filename match
     */
    private function find_attachment_by_filename($file_path) {
        global $wpdb;
        
        $filename = basename($file_path);
        $upload_dir = wp_upload_dir();
        $base_dir = $upload_dir['basedir'];
        
        $query = $wpdb->prepare(
            "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value LIKE %s",
            '%' . $wpdb->esc_like($filename) . '%'
        );
        
        $attachment_ids = $wpdb->get_col($query);
        
        foreach ($attachment_ids as $attachment_id) {
            $attached_file = get_post_meta($attachment_id, '_wp_attached_file', true);
            $full_path = $base_dir . '/' . $attached_file;
            
            if (file_exists($full_path)) {
                // Compare file content to ensure it's the same file
                if (md5_file($full_path) === md5_file($file_path)) {
                    error_log('Cloud Uploads: Found attachment by filename match: ' . $attachment_id);
                    return (int) $attachment_id;
                }
            }
        }
        
        return 0;
    }
    
    /**
     * Method 4: Find attachment by post title
     */
    private function find_attachment_by_post_title($file_path) {
        global $wpdb;
        
        $filename = basename($file_path);
        $filename_without_ext = pathinfo($filename, PATHINFO_FILENAME);
        
        $query = $wpdb->prepare(
            "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_title = %s",
            $filename_without_ext
        );
        
        $attachment_id = $wpdb->get_var($query);
        
        if ($attachment_id) {
            // Verify by checking the attached file
            $attached_file = get_post_meta($attachment_id, '_wp_attached_file', true);
            if ($attached_file) {
                $upload_dir = wp_upload_dir();
                $full_path = $upload_dir['basedir'] . '/' . $attached_file;
                if (file_exists($full_path) && md5_file($full_path) === md5_file($file_path)) {
                    error_log('Cloud Uploads: Found attachment by post title: ' . $attachment_id);
                    return (int) $attachment_id;
                }
            }
        }
        
        return 0;
    }
    
    /**
     * Method 5: Enhanced - Find recent attachments (last 10 minutes)
     */
    private function find_recent_attachment($file_path) {
        global $wpdb;
        
        $filename = basename($file_path);
        
        // Look for attachments created in the last 10 minutes
        $time_cutoff = date('Y-m-d H:i:s', time() - 600); // 10 minutes ago
        
        $query = $wpdb->prepare(
            "SELECT ID FROM $wpdb->posts 
             WHERE post_type = 'attachment' 
             AND post_date > %s 
             ORDER BY post_date DESC 
             LIMIT 20",
            $time_cutoff
        );
        
        $recent_attachment_ids = $wpdb->get_col($query);
        
        foreach ($recent_attachment_ids as $attachment_id) {
            $attached_file = get_post_meta($attachment_id, '_wp_attached_file', true);
            
            if ($attached_file) {
                $upload_dir = wp_upload_dir();
                $full_path = $upload_dir['basedir'] . '/' . $attached_file;
                
                // Check if this could be our file
                if (file_exists($full_path)) {
                    // Compare file sizes first (faster than MD5)
                    if (filesize($full_path) === filesize($file_path)) {
                        // If sizes match, compare content
                        if (md5_file($full_path) === md5_file($file_path)) {
                            error_log('Cloud Uploads: Found recent attachment by content match: ' . $attachment_id);
                            return (int) $attachment_id;
                        }
                    }
                }
            }
            
            // Also check if the filename matches the post title
            $post = get_post($attachment_id);
            if ($post && stripos($post->post_title, pathinfo($filename, PATHINFO_FILENAME)) !== false) {
                error_log('Cloud Uploads: Found recent attachment by title match: ' . $attachment_id);
                return (int) $attachment_id;
            }
        }
        
        return 0;
    }
    
    /**
     * Method 6: Content-based matching for difficult cases
     */
    private function find_attachment_by_content_match($file_path) {
        global $wpdb;
        
        if (!file_exists($file_path)) {
            return 0;
        }
        
        $file_size = filesize($file_path);
        $file_hash = md5_file($file_path);
        
        // Look for attachments with similar file size
        $query = $wpdb->prepare(
            "SELECT pm.post_id 
             FROM $wpdb->postmeta pm 
             INNER JOIN $wpdb->posts p ON pm.post_id = p.ID 
             WHERE pm.meta_key = '_wp_attached_file' 
             AND p.post_type = 'attachment' 
             LIMIT 50" // Limit to recent attachments
        );
        
        $attachment_ids = $wpdb->get_col($query);
        
        foreach ($attachment_ids as $attachment_id) {
            $attached_file = get_post_meta($attachment_id, '_wp_attached_file', true);
            
            if ($attached_file) {
                $upload_dir = wp_upload_dir();
                $full_path = $upload_dir['basedir'] . '/' . $attached_file;
                
                if (file_exists($full_path)) {
                    // Quick size check first
                    if (filesize($full_path) === $file_size) {
                        // Then hash check
                        if (md5_file($full_path) === $file_hash) {
                            error_log('Cloud Uploads: Found attachment by content hash match: ' . $attachment_id);
                            return (int) $attachment_id;
                        }
                    }
                }
            }
        }
        
        return 0;
    }

    /**
     * Increase the threshold for big images to reduce server load
     */
    public function increase_big_image_threshold($threshold, $imagesize, $file, $attachment_id) {
        return 3000; // Increase from default 2560 to 3000 pixels
    }

    /**
     * Handle AJAX uploads to ensure S3 sync happens
     */
    public function handle_ajax_upload() {
        // Hook into the AJAX response to sync after upload
        add_filter('wp_ajax_upload_attachment_response', array($this, 'sync_after_ajax_upload'), 10, 2);
    }
    
    /**
     * Adjust image quality to reduce processing load
     */
    public function adjust_image_quality($quality, $mime_type) {
        return 82; // Slightly reduce quality to save resources
    }
    
    /**
     * Adjust JPEG quality specifically
     */
    public function adjust_jpeg_quality($quality, $context) {
        return 82;
    }
    
    /**
     * Get current memory limit in bytes
     */
    private function get_memory_limit() {
        $memory_limit = ini_get('memory_limit');
        if (preg_match('/^(\d+)(.)$/', $memory_limit, $matches)) {
            if ($matches[2] == 'M') {
                return $matches[1] * 1024 * 1024;
            } elseif ($matches[2] == 'K') {
                return $matches[1] * 1024;
            }
        }
        return 64 * 1024 * 1024; // Default to 64MB if unable to determine
    }
    
    /**
     * Check if server has enough resources to process image
     */
    private function has_sufficient_resources($file_size) {
        // Check memory usage
        $memory_used = memory_get_usage(true);
        $memory_available = $this->memory_limit - $memory_used;
        
        // Estimate memory needed for image processing (roughly 3x file size)
        $memory_needed = $file_size * 3;
        
        if ($memory_needed > $memory_available) {
            error_log('Cloud Uploads: Insufficient memory. Available: ' . round($memory_available/1024/1024, 2) . 'MB, Needed: ' . round($memory_needed/1024/1024, 2) . 'MB');
            return false;
        }
        
        return true;
    }
    
    /**
     * Increase memory limit temporarily for image processing
     */
    private function boost_memory_limit() {
        $current_limit = ini_get('memory_limit');
        $current_bytes = $this->get_memory_limit();
        
        // Only increase if current limit is less than 256MB
        if ($current_bytes < (256 * 1024 * 1024)) {
            @ini_set('memory_limit', '256M');
            return true;
        }
        
        return false;
    }
    
    public function sync_attachment($attachment_id) {
        // Check if already synced to avoid duplicate syncs
        if (get_post_meta($attachment_id, '_s3_synced', true)) {
            return true;
        }
        
        $file_path = get_attached_file($attachment_id);
        
        if (!$file_path || !file_exists($file_path)) {
            error_log('Cloud Uploads: File not found for attachment ID: ' . $attachment_id);
            return false;
        }
        
        $file_size = filesize($file_path);
        
        // Check if we have enough resources, boost if needed
        if (!$this->has_sufficient_resources($file_size)) {
            $this->boost_memory_limit();
            
            // Check again after boosting
            if (!$this->has_sufficient_resources($file_size)) {
                error_log('Cloud Uploads: Still insufficient resources after boosting memory');
                
                // Schedule for later processing
                $this->schedule_deferred_sync($attachment_id);
                return false;
            }
        }
        
        // Main file upload
        $result = $this->upload_to_s3($file_path, $attachment_id);
        
        if ($result) {
            update_post_meta($attachment_id, '_s3_synced', true);
            update_post_meta($attachment_id, '_s3_url', $result);
            return true;
        }
        
        return false;
    }
    
    /**
     * Schedule deferred sync for when server resources are available
     */
    private function schedule_deferred_sync($attachment_id) {
        $deferred_syncs = get_option('cloud_uploads_deferred', array());
        $deferred_syncs[] = array(
            'attachment_id' => $attachment_id,
            'scheduled_time' => time() + 300, // 5 minutes from now
            'attempts' => 0
        );
        
        update_option('cloud_uploads_deferred', $deferred_syncs);
        
        // Schedule cron event if not already scheduled
        if (!wp_next_scheduled('cloud_uploads_process_deferred')) {
            wp_schedule_event(time() + 300, 'five_minutes', 'cloud_uploads_process_deferred');
        }
        
        error_log('Cloud Uploads: Deferred sync scheduled for attachment ' . $attachment_id);
    }
    
    public function sync_attachment_metadata($metadata, $attachment_id) {
        // Sync the main file if not already synced
        if (!get_post_meta($attachment_id, '_s3_synced', true)) {
            $this->sync_attachment($attachment_id);
        }
        
        // Sync all image sizes (but only if we have resources)
        if ($this->has_sufficient_resources(0)) { // 0 means just check current memory
            $this->sync_attachment_sizes($metadata, $attachment_id);
        } else {
            // Schedule sizes sync for later
            $this->schedule_sizes_sync($attachment_id, $metadata);
        }
        
        return $metadata;
    }
    
    /**
     * Schedule image sizes sync for later
     */
    private function schedule_sizes_sync($attachment_id, $metadata) {
        $sizes_syncs = get_option('cloud_uploads_sizes_deferred', array());
        $sizes_syncs[] = array(
            'attachment_id' => $attachment_id,
            'metadata' => $metadata,
            'scheduled_time' => time() + 300, // 5 minutes from now
        );
        
        update_option('cloud_uploads_sizes_deferred', $sizes_syncs);
        
        // Schedule cron event if not already scheduled
        if (!wp_next_scheduled('cloud_uploads_process_sizes')) {
            wp_schedule_event(time() + 300, 'five_minutes', 'cloud_uploads_process_sizes');
        }
    }
    
    private function sync_attachment_sizes($metadata, $attachment_id) {
        if (empty($metadata['sizes']) || !is_array($metadata['sizes'])) {
            return;
        }
        
        $upload_dir = wp_upload_dir();
        $file_dir = dirname(get_attached_file($attachment_id));
        
        foreach ($metadata['sizes'] as $size) {
            $file_path = trailingslashit($file_dir) . $size['file'];
            
            if (file_exists($file_path)) {
                $this->upload_to_s3($file_path, $attachment_id, $size['file']);
            }
        }
    }
    
    private function upload_to_s3($file_path, $attachment_id, $relative_path = null) {
        $upload_dir = wp_upload_dir();
        
        if ($relative_path === null) {
            // Main file
            $relative_path = str_replace($upload_dir['basedir'] . '/', '', $file_path);
        } else {
            // Image size - need to construct full relative path
            $file_dir = str_replace($upload_dir['basedir'] . '/', '', dirname(get_attached_file($attachment_id)));
            $relative_path = trailingslashit($file_dir) . $relative_path;
        }
        
        // Add path prefix if set
        $path_prefix = $this->settings->get_option('path_prefix');
        $s3_path = !empty($path_prefix) ? $path_prefix . $relative_path : $relative_path;
        
        try {
            $result = $this->s3_client->upload_file($file_path, $s3_path);
            
            if ($result) {
                // Store S3 path for potential deletion
                $s3_paths = get_post_meta($attachment_id, '_s3_paths', true);
                if (!is_array($s3_paths)) {
                    $s3_paths = array();
                }
                
                if (!in_array($s3_path, $s3_paths)) {
                    $s3_paths[] = $s3_path;
                    update_post_meta($attachment_id, '_s3_paths', $s3_paths);
                }
                
                return $result;
            }
        } catch (Exception $e) {
            error_log('Cloud Uploads: Error uploading file to S3 - ' . $e->getMessage());
        }
        
        return false;
    }
    
    public function delete_attachment($attachment_id) {
        $s3_paths = get_post_meta($attachment_id, '_s3_paths', true);
        
        if (is_array($s3_paths) && $this->settings->get_option('delete_remote')) {
            foreach ($s3_paths as $s3_path) {
                $this->s3_client->delete_file($s3_path);
            }
        }
        
        // Clean up metadata
        delete_post_meta($attachment_id, '_s3_synced');
        delete_post_meta($attachment_id, '_s3_url');
        delete_post_meta($attachment_id, '_s3_paths');
    }
    
    public function handle_bulk_actions() {
        if (isset($_GET['action']) && $_GET['action'] === 'sync_all_to_s3') {
            check_admin_referer('sync_all_to_sync');
            $this->sync_all_attachments();
            wp_redirect(admin_url('upload.php?page=cloud-uploads&synced=all'));
            exit;
        }
    }
    
    public function sync_all_attachments() {
        $attachments = get_posts(array(
            'post_type' => 'attachment',
            'posts_per_page' => -1,
            'post_status' => 'inherit',
            'fields' => 'ids'
        ));
        
        foreach ($attachments as $attachment_id) {
            // Only sync if not already synced
            if (!get_post_meta($attachment_id, '_s3_synced', true)) {
                $this->sync_attachment($attachment_id);
                
                // Also sync all image sizes
                $metadata = wp_get_attachment_metadata($attachment_id);
                if ($metadata) {
                    $this->sync_attachment_sizes($metadata, $attachment_id);
                }
            }
        }
    }
    
    /**
     * Process deferred syncs
     */
    public function process_deferred_syncs() {
        $deferred_syncs = get_option('cloud_uploads_deferred', array());
        $processed = array();
        
        foreach ($deferred_syncs as $index => $sync) {
            if ($sync['scheduled_time'] <= time() && $sync['attempts'] < 3) {
                // Try to sync
                $success = $this->sync_attachment($sync['attachment_id']);
                
                if ($success) {
                    // Remove from deferred list
                    $processed[] = $index;
                } else {
                    // Update attempts and reschedule
                    $deferred_syncs[$index]['attempts']++;
                    $deferred_syncs[$index]['scheduled_time'] = time() + 300; // 5 more minutes
                }
            }
        }
        
        // Remove processed syncs
        foreach ($processed as $index) {
            unset($deferred_syncs[$index]);
        }
        
        update_option('cloud_uploads_deferred', array_values($deferred_syncs));
    }
    
    /**
     * Process deferred sizes syncs
     */
    public function process_deferred_sizes() {
        $deferred_sizes = get_option('cloud_uploads_sizes_deferred', array());
        $processed = array();
        
        foreach ($deferred_sizes as $index => $size_sync) {
            if ($size_sync['scheduled_time'] <= time()) {
                $this->sync_attachment_sizes($size_sync['metadata'], $size_sync['attachment_id']);
                $processed[] = $index;
            }
        }
        
        // Remove processed syncs
        foreach ($processed as $index) {
            unset($deferred_sizes[$index]);
        }
        
        update_option('cloud_uploads_sizes_deferred', array_values($deferred_sizes));
    }
}