<?php
/**
 * Centralized File Management System
 * Handles upload, replacement, deletion for both free and premium tiers
 */

defined('ABSPATH') || exit;

class SPDFED_File_Manager {
    
    /**
     * Get the private upload directory path
     */
    public static function get_upload_dir(): string {
        return WP_CONTENT_DIR . '/private-uploads/';
    }
    
    /**
     * Ensure upload directory exists and is secure
     */
    public static function ensure_upload_dir(): bool {
        $dir = self::get_upload_dir();
        
        if (!file_exists($dir)) {
            if (!wp_mkdir_p($dir)) {
                return false;
            }
            
            // Create .htaccess for security
            $htaccess_content = "# Secure PDF Email Download - Block direct access\n";
            $htaccess_content .= "Order deny,allow\n";
            $htaccess_content .= "Deny from all\n";
            file_put_contents($dir . '.htaccess', $htaccess_content);
            
            // Create index.php for security
            file_put_contents($dir . 'index.php', "<?php\n// Silence is golden.\n");
        }
        
        return true;
    }
    
    /**
     * Upload a file for free tier user
     * 
     * @param array $file_data $_FILES array data
     * @return array Success/error result
     */
    public static function upload_free_file(array $file_data): array {
        try {
            // Remove old file first
            self::delete_free_file();
            
            if (!self::ensure_upload_dir()) {
                throw new Exception(__('Could not create upload directory.', 'email-gated-downloads'));
            }
            
            // Validate file
            $validation = self::validate_file($file_data);
            if (!$validation['success']) {
                throw new Exception($validation['message']);
            }
            
            // CAPTURE ORIGINAL FILENAME BEFORE UUID GENERATION
            $original_filename = sanitize_file_name($file_data['name']);
            
            // Handle upload with UUID filename generation
            add_filter('upload_dir', [self::class, 'filter_upload_dir']);
            add_filter('wp_handle_upload_prefilter', [self::class, 'generate_uuid_filename']);
            $movefile = wp_handle_upload($file_data, [
                'test_form' => false,
                'mimes' => [
                    'pdf' => 'application/pdf',
                    'zip' => 'application/zip',
                    'zip2' => 'application/x-zip-compressed',
                    'zip3' => 'application/x-zip',
                ],
            ]);
            remove_filter('wp_handle_upload_prefilter', [self::class, 'generate_uuid_filename']);
            remove_filter('upload_dir', [self::class, 'filter_upload_dir']);
            
            if (isset($movefile['error'])) {
                throw new Exception($movefile['error']);
            }
            
            // Store file metadata
            $storage_filename = basename($movefile['file']);
            
            update_option('spdfed_free_storage_filename', $storage_filename);
            update_option('spdfed_free_file_name', $original_filename);
            update_option('spdfed_free_mime_type', $movefile['type']);
            
            return [
                'success' => true,
                'message' => __('File uploaded successfully.', 'email-gated-downloads'),
                'storage_filename' => $storage_filename,
                'original_filename' => $original_filename,
                'mime_type' => $movefile['type']
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
    
    /**
     * Upload a file for premium tier CPT
     * 
     * @param array $file_data $_FILES array data
     * @param int $post_id CPT post ID
     * @return array Success/error result
     */
    public static function upload_cpt_file(array $file_data, int $post_id): array {
        try {
            if (!$post_id || get_post_type($post_id) !== 'spdfed_secure_file') {
                throw new Exception(__('Invalid post ID.', 'email-gated-downloads'));
            }
            
            // Remove old file first
            self::delete_cpt_file($post_id);
            
            if (!self::ensure_upload_dir()) {
                throw new Exception(__('Could not create upload directory.', 'email-gated-downloads'));
            }
            
            // Validate file
            $validation = self::validate_file($file_data);
            if (!$validation['success']) {
                throw new Exception($validation['message']);
            }
            
            // CAPTURE ORIGINAL FILENAME BEFORE UUID GENERATION
            $original_filename = sanitize_file_name($file_data['name']);
            
            // Handle upload with UUID filename generation
            add_filter('upload_dir', [self::class, 'filter_upload_dir']);
            add_filter('wp_handle_upload_prefilter', [self::class, 'generate_uuid_filename']);
            $movefile = wp_handle_upload($file_data, [
                'test_form' => false,
                'mimes' => [
                    'pdf' => 'application/pdf',
                    'zip' => 'application/zip',
                    'zip2' => 'application/x-zip-compressed',
                    'zip3' => 'application/x-zip',
                ],
            ]);
            remove_filter('wp_handle_upload_prefilter', [self::class, 'generate_uuid_filename']);
            remove_filter('upload_dir', [self::class, 'filter_upload_dir']);
            
            if (isset($movefile['error'])) {
                throw new Exception($movefile['error']);
            }
            
            // Store file metadata
            $storage_filename = basename($movefile['file']);
            
            update_post_meta($post_id, '_spdfed_storage_filename', $storage_filename);
            update_post_meta($post_id, '_spdfed_file_name', $original_filename);
            update_post_meta($post_id, '_spdfed_original_file_name', $original_filename);
            update_post_meta($post_id, '_spdfed_mime_type', $movefile['type']);
            
            // Clear cache for CPT files count and filenames
            wp_cache_delete('spdfed_cpt_files_count');
            wp_cache_delete('spdfed_cpt_storage_filenames');
            
            // Clear any temporary removal notice if this was the default post
            $file_removed_notice = get_option('spdfed_default_post_file_removed', 0);
            if ($file_removed_notice == $post_id) {
                delete_option('spdfed_default_post_file_removed');
            }
            
            return [
                'success' => true,
                'message' => __('File uploaded successfully.', 'email-gated-downloads'),
                'storage_filename' => $storage_filename,
                'original_filename' => $original_filename,
                'mime_type' => $movefile['type']
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
    
    /**
     * Delete free tier file
     */
    public static function delete_free_file(): bool {
        $storage_filename = get_option('spdfed_free_storage_filename');
        
        if ($storage_filename) {
            $file_path = self::get_upload_dir() . $storage_filename;
            $deleted = self::safe_delete_file($file_path);
            
            // Clean up options even if file deletion failed
            delete_option('spdfed_free_storage_filename');
            delete_option('spdfed_free_file_name');
            delete_option('spdfed_free_mime_type');
            
            return $deleted;
        }
        
        return true;
    }
    
    /**
     * Delete CPT file
     */
    public static function delete_cpt_file(int $post_id): bool {
        if (!$post_id || get_post_type($post_id) !== 'spdfed_secure_file') {
            return false;
        }
        
        $storage_filename = get_post_meta($post_id, '_spdfed_storage_filename', true);
        
        // Check if this is the default post for premium shortcodes
        $default_post_id = absint(get_option('spdfed_premium_default_post_id', 0));
        $is_default_post = ($default_post_id === $post_id);
        
        if ($storage_filename) {
            $file_path = self::get_upload_dir() . $storage_filename;
            $deleted = self::safe_delete_file($file_path);
            
            // Clean up meta even if file deletion failed
            delete_post_meta($post_id, '_spdfed_storage_filename');
            delete_post_meta($post_id, '_spdfed_file_name');
            delete_post_meta($post_id, '_spdfed_original_file_name');
            delete_post_meta($post_id, '_spdfed_mime_type');
            
            // Clear cache for CPT files count and filenames
            wp_cache_delete('spdfed_cpt_files_count');
            wp_cache_delete('spdfed_cpt_storage_filenames');
            
            // If this was the default post, add a notice but DON'T clear the default
            // This allows the shortcode to work again when user uploads a new file
            if ($is_default_post) {
                update_option('spdfed_default_post_file_removed', $post_id);
            }
            
            return $deleted;
        }
        
        return true;
    }
    
    /**
     * Get file info for free tier
     */
    public static function get_free_file_info(): ?array {
        $storage_filename = get_option('spdfed_free_storage_filename');
        
        if (!$storage_filename) {
            return null;
        }
        
        return [
            'storage_filename' => $storage_filename,
            'original_filename' => get_option('spdfed_free_file_name', ''),
            'mime_type' => get_option('spdfed_free_mime_type', ''),
            'file_path' => self::get_upload_dir() . $storage_filename,
            'exists' => file_exists(self::get_upload_dir() . $storage_filename)
        ];
    }
    
    /**
     * Get file info for CPT
     */
    public static function get_cpt_file_info(int $post_id): ?array {
        if (!$post_id || get_post_type($post_id) !== 'spdfed_secure_file') {
            return null;
        }
        
        $storage_filename = get_post_meta($post_id, '_spdfed_storage_filename', true);
        
        if (!$storage_filename) {
            return null;
        }
        
        return [
            'storage_filename' => $storage_filename,
            'original_filename' => get_post_meta($post_id, '_spdfed_file_name', true),
            'mime_type' => get_post_meta($post_id, '_spdfed_mime_type', true),
            'file_path' => self::get_upload_dir() . $storage_filename,
            'exists' => file_exists(self::get_upload_dir() . $storage_filename)
        ];
    }
    
    /**
     * Find orphaned files (files without corresponding database records)
     */
    public static function find_orphaned_files(): array {
        $upload_dir = self::get_upload_dir();
        
        if (!is_dir($upload_dir)) {
            return [];
        }
        
        // Get all files in upload directory
        $all_files = array_diff(scandir($upload_dir), ['.', '..', '.htaccess', 'index.php']);
        
        // Get files referenced in database
        $referenced_files = [];
        
        // Free tier file
        $free_file = get_option('spdfed_free_storage_filename');
        if ($free_file) {
            $referenced_files[] = $free_file;
        }
        
        // CPT files with caching
        $cache_key = 'spdfed_cpt_storage_filenames';
        $cpt_files = wp_cache_get($cache_key);
        
        if (false === $cpt_files) {
            global $wpdb;
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            $cpt_files = $wpdb->get_col(
                $wpdb->prepare(
                    "SELECT meta_value FROM {$wpdb->postmeta} 
                     WHERE meta_key = %s 
                     AND meta_value != %s",
                    '_spdfed_storage_filename',
                    ''
                )
            );
            wp_cache_set($cache_key, $cpt_files, '', 300); // Cache for 5 minutes
        }
        
        $referenced_files = array_merge($referenced_files, $cpt_files);
        
        // Find orphaned files
        return array_diff($all_files, $referenced_files);
    }
    
    /**
     * Clean up orphaned files
     */
    public static function cleanup_orphaned_files(): array {
        $orphaned_files = self::find_orphaned_files();
        $deleted_files = [];
        $failed_files = [];
        
        foreach ($orphaned_files as $filename) {
            $file_path = self::get_upload_dir() . $filename;
            
            if (self::safe_delete_file($file_path)) {
                $deleted_files[] = $filename;
            } else {
                $failed_files[] = $filename;
            }
        }
        
        return [
            'deleted' => $deleted_files,
            'failed' => $failed_files,
            'total_orphaned' => count($orphaned_files)
        ];
    }
    
    /**
     * Validate uploaded file
     */
    private static function validate_file(array $file_data): array {
        // Check for upload errors
        if ($file_data['error'] !== UPLOAD_ERR_OK) {
            return [
                'success' => false,
                'message' => self::get_upload_error_message($file_data['error'])
            ];
        }
        
        
        // Check file type
        $allowed_types = ['application/pdf', 'application/zip', 'application/x-zip-compressed', 'application/x-zip'];
        $file_type = wp_check_filetype($file_data['name']);
        
        if (!in_array($file_type['type'], $allowed_types, true)) {
            return [
                'success' => false,
                'message' => __('Only PDF and ZIP files are allowed.', 'email-gated-downloads')
            ];
        }
        
        return ['success' => true];
    }
    
    /**
     * Safely delete a file with security checks
     */
    private static function safe_delete_file(string $file_path): bool {
        if (!file_exists($file_path)) {
            return true; // Already deleted
        }
        
        // Security check: ensure file is within our upload directory
        $upload_dir = realpath(self::get_upload_dir());
        $file_real_path = realpath($file_path);
        
        if (!$file_real_path || strpos($file_real_path, $upload_dir) !== 0) {
            return false; // Path traversal attempt
        }
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
        return @unlink($file_path);
    }
    
    /**
     * Filter upload directory to our private directory
     */
    public static function filter_upload_dir(array $dir): array {
        $private_dir = self::get_upload_dir();
        
        return [
            'path'   => $private_dir,
            'url'    => '', // No direct URL access
            'subdir' => '',
            'basedir' => $private_dir,
            'baseurl' => '',
            'error'  => false
        ];
    }
    
    /**
     * Generate UUID filename for uploaded files
     * This ensures files are stored with obfuscated names on the server
     */
    public static function generate_uuid_filename(array $file): array {
        // Get file extension
        $pathinfo = pathinfo($file['name']);
        $extension = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
        
        // Generate UUID v4
        $uuid = sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            wp_rand(0, 0xffff),
            wp_rand(0, 0xffff),
            wp_rand(0, 0xffff),
            wp_rand(0, 0x0fff) | 0x4000,
            wp_rand(0, 0x3fff) | 0x8000,
            wp_rand(0, 0xffff),
            wp_rand(0, 0xffff),
            wp_rand(0, 0xffff)
        );
        
        // Set the new filename with UUID
        $file['name'] = $uuid . $extension;
        
        return $file;
    }
    
    /**
     * Get human-readable upload error message
     */
    public static function get_upload_error_message(int $error_code): string {
        $upload_errors = [
            UPLOAD_ERR_INI_SIZE   => __('File is too large (exceeds upload_max_filesize).', 'email-gated-downloads'),
            UPLOAD_ERR_FORM_SIZE  => __('File is too large (exceeds MAX_FILE_SIZE).', 'email-gated-downloads'),
            UPLOAD_ERR_PARTIAL    => __('File was only partially uploaded.', 'email-gated-downloads'),
            UPLOAD_ERR_NO_FILE    => __('No file was uploaded.', 'email-gated-downloads'),
            UPLOAD_ERR_NO_TMP_DIR => __('Missing temporary upload directory.', 'email-gated-downloads'),
            UPLOAD_ERR_CANT_WRITE => __('Failed to write file to disk.', 'email-gated-downloads'),
            UPLOAD_ERR_EXTENSION  => __('File upload stopped by extension.', 'email-gated-downloads'),
        ];
        
        return $upload_errors[$error_code] ?? __('Unknown upload error.', 'email-gated-downloads');
    }
    
    /**
     * Fix existing files that may have missing or incorrect filename metadata
     * This repairs files that were uploaded before the UUID system was implemented
     */
    public static function repair_file_metadata(): array {
        global $wpdb;
        
        $repaired = [];
        $failed = [];
        
        // Get all secure file posts with storage filename meta (optimized query)
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $post_ids = $wpdb->get_col($wpdb->prepare("
            SELECT DISTINCT p.ID 
            FROM {$wpdb->posts} p
            INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
            WHERE p.post_type = %s 
            AND p.post_status = %s
            AND pm.meta_key = %s
            ORDER BY p.ID ASC
        ", 'spdfed_secure_file', 'publish', '_spdfed_storage_filename'));
        
        if (empty($post_ids)) {
            return ['repaired' => [], 'failed' => []];
        }
        
        // Get post objects for the found IDs
        $posts = get_posts([
            'post_type' => 'spdfed_secure_file',
            'post_status' => 'publish',
            'include' => $post_ids,
            'numberposts' => -1
        ]);
        
        foreach ($posts as $post) {
            $post_id = $post->ID;
            $storage_filename = get_post_meta($post_id, '_spdfed_storage_filename', true);
            $current_display_name = get_post_meta($post_id, '_spdfed_file_name', true);
            $current_original_name = get_post_meta($post_id, '_spdfed_original_file_name', true);
            
            $needs_repair = false;
            $display_name_to_use = '';
            
            // Check if display name is missing or is a UUID
            if (empty($current_display_name) || self::looks_like_uuid($current_display_name)) {
                $needs_repair = true;
                
                // Try to get original name from other metadata
                if (!empty($current_original_name) && !self::looks_like_uuid($current_original_name)) {
                    $display_name_to_use = $current_original_name;
                } elseif (!empty($storage_filename) && !self::looks_like_uuid($storage_filename)) {
                    // Storage filename doesn't look like UUID, might be the original name
                    $display_name_to_use = $storage_filename;
                } else {
                    // Extract name from post title as last resort
                    $post_title = get_the_title($post_id);
                    if (!empty($post_title)) {
                        $display_name_to_use = sanitize_file_name($post_title . '.pdf');
                    } else {
                        $display_name_to_use = 'secure-download.pdf';
                    }
                }
            }
            
            if ($needs_repair) {
                // Update the metadata
                update_post_meta($post_id, '_spdfed_file_name', $display_name_to_use);
                update_post_meta($post_id, '_spdfed_original_file_name', $display_name_to_use);
                
                $repaired[] = [
                    'post_id' => $post_id,
                    'post_title' => get_the_title($post_id),
                    'old_name' => $current_display_name,
                    'new_name' => $display_name_to_use
                ];
            }
        }
        
        return [
            'repaired' => $repaired,
            'failed' => $failed,
            'total_checked' => count($posts)
        ];
    }
    
    /**
     * Check if a filename looks like a UUID
     */
    private static function looks_like_uuid(string $filename): bool {
        // Remove extension for checking
        $name_without_ext = pathinfo($filename, PATHINFO_FILENAME);
        
        // UUID v4 pattern: 8-4-4-4-12 characters (36 total with hyphens)
        return strlen($name_without_ext) === 36 && substr_count($name_without_ext, '-') === 4;
    }
}
