<?php

/**
 * JCOGS Image Pro - Preview Route
 * ===============================
 * AJAX endpoint for live preview generation using Action Link system
 * 
 * @category   ExpressionEngine Add-on
 * @package    JCOGS Image Pro
 * @author     JCOGS Design <contact@jcogs.net>
 * @copyright  Copyright (c) 2021 - 2025 JCOGS Design
 * @license    https://jcogs.net/add-ons/license/jcogs_img_pro
 * @version    2.0.6
 * @link       https://JCOGS.net/
 * @since      2.0.0
 */

namespace JCOGSDesign\JCOGSImagePro\ControlPanel\Routes\Presets;

use Exception;
use JCOGSDesign\JCOGSImagePro\ControlPanel\Routes\ImageAbstractRoute;
use JCOGSDesign\JCOGSImagePro\Service\ParameterRegistry;

class Preview extends ImageAbstractRoute
{
    /**
     * @var string Route path for URL generation
     */
    protected $route_path = 'presets/preview';

    /**
     * Process AJAX preview request
     * 
     * Generates action link URL for real-time preview using existing
     * _serve_act_image system for efficient image delivery.
     * 
     * @param mixed $preset_id Preset ID (optional for validation)
     * @return $this Fluent interface for EE7 routing
     */
    public function process($preset_id = false)
    {
        // Ensure this is an AJAX POST request
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->utilities_service->debug_message('preview_invalid_request_method');
            $this->_returnJsonError('Invalid request method');
            return $this;
        }

        // Check for AJAX headers
        if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || 
            strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
            $this->utilities_service->debug_message('preview_missing_ajax_headers');
            $this->_returnJsonError('AJAX request required');
            return $this;
        }

        try {
            // Get JSON input data
            $json_input = file_get_contents('php://input');
            $this->utilities_service->debug_message('preview_json_input', $json_input);
            
            $request_data = json_decode($json_input, true);
            
            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->utilities_service->debug_message('preview_json_decode_error', json_last_error_msg());
                $this->_returnJsonError('Invalid JSON data');
                return $this;
            }

            // Validate CSRF token
            $csrf_token = $request_data['csrf_token'] ?? '';
            if (empty($csrf_token) || $csrf_token !== CSRF_TOKEN) {
                $this->utilities_service->debug_message('preview_csrf_validation_failed', CSRF_TOKEN, $csrf_token);
                $this->_returnJsonError('Invalid CSRF token');
                return $this;
            }

            // Extract parameters and preview image
            $parameters = $request_data['parameters'] ?? [];
            $preview_file_id = $request_data['preview_file_id'] ?? null;
            
            $this->utilities_service->debug_message('preview_parameters_count', count($parameters));
            $this->utilities_service->debug_message('preview_file_id', var_export($preview_file_id, true));
            
            if (empty($parameters)) {
                $this->utilities_service->debug_message('preview_no_parameters');
                $this->_returnJsonError('No parameters provided');
                return $this;
            }

            // Validate preview image
            $preview_image_url = $this->_getPreviewImageUrl($preview_file_id);
            $this->utilities_service->debug_message('preview_image_url_result', var_export($preview_image_url, true));
            
            if (!$preview_image_url) {
                $this->utilities_service->debug_message('preview_image_url_empty');
                $this->_returnJsonError('Invalid preview image');
                return $this;
            }

            // Build action link for preview
            $preview_url = $this->_generatePreviewActionLink($parameters, $preview_image_url);
            $this->utilities_service->debug_message('preview_generated_url', var_export($preview_url, true));
            
            if (!$preview_url) {
                $this->_returnJsonError('Failed to generate preview');
                return $this;
            }

            // Return success response
            $this->_returnJsonSuccess([
                'preview_url' => $preview_url,
                'timestamp' => time(),
                'cache_buster' => uniqid()
            ]);

        } catch (Exception $e) {
            $this->utilities_service->debug_log('Preview generation error: ' . $e->getMessage());
            $this->_returnJsonError('Preview generation failed: ' . $e->getMessage());
        }

        return $this;
    }

    /**
     * Generate action link URL for preview processing
     * 
     * Leverages existing action link system from OutputStage for consistent
     * image processing and delivery.
     * 
     * @param array $parameters Processing parameters
     * @param string $image_url Source image URL
     * @return string|null Action link URL or null on failure
     */
    private function _generatePreviewActionLink(array $parameters, string $image_url): ?string
    {
        try {
            // Build parameters for action link generation
            $action_params = $parameters;
            
            // Set required parameters for action link
            $action_params['src'] = $image_url;
            $action_params['action_link'] = 'yes'; // Force action link generation
            $action_params['cache'] = '0'; // Disable caching for previews
            $action_params['url_only'] = 'yes'; // Force URL only mode
            
            // Add preview-specific parameters
            $action_params['act_what'] = 'preview';
            $action_params['act_path'] = $image_url;
            
            // Validate critical parameters using ParameterRegistry
            $action_params = $this->_validatePreviewParameters($action_params);
            
            // Build ACT packet (base64 encoded JSON)
            $act_packet = base64_encode(json_encode($action_params));
            
            if (empty($act_packet)) {
                $this->utilities_service->debug_log('Preview action link: JSON encoding failed');
                return null;
            }

            // Build CP route URL so preview image delivery stays on CP request path.
            $preview_url = ee('CP/URL')->make('addons/settings/jcogs_img_pro/presets/preview_image', [
                'act_packet' => $act_packet,
                'preview' => 1,
                't' => time(), // Cache buster
            ])->compile();

            $this->utilities_service->debug_log('Generated preview image URL: ' . $preview_url);
            return $preview_url;

        } catch (Exception $e) {
            $this->utilities_service->debug_log('Preview action link generation failed: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Get file info from EE Files
     * 
     * @param int $file_id EE Files ID
     * @return array|null File information or null if not found
     */
    private function _getFileInfo(int $file_id): ?array
    {
        try {
            $file = ee('Model')->get('File', $file_id)->first();
            
            if (!$file) {
                return null;
            }

            return [
                'id' => $file->file_id,
                'url' => $file->getAbsoluteURL(),
                'filename' => $file->file_name,
                'mime_type' => $file->mime_type
            ];

        } catch (Exception $e) {
            $this->utilities_service->debug_log('File lookup error: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Get preview image URL from file ID
     * 
     * @param int|null $preview_file_id EE Files ID
     * @return string|null Image URL or null if invalid
     */
    private function _getPreviewImageUrl($preview_file_id): ?string
    {
        if (empty($preview_file_id)) {
            // Try to get global default preview image
            $default_preview_setting = $this->settings_service->get('preset_default_preview_file_id', null);
            $this->utilities_service->debug_log('Preview URL: No file ID provided, checking default setting: ' . var_export($default_preview_setting, true));
            
            if (empty($default_preview_setting)) {
                $this->utilities_service->debug_log('Preview URL: No default preview setting found');
                return null;
            }
            
            // Parse EE Files format: {file:97:url} -> extract file ID
            if (preg_match('/\{file:(\d+):url\}/', $default_preview_setting, $matches)) {
                $preview_file_id = (int)$matches[1];
                $this->utilities_service->debug_log('Preview URL: Extracted file ID from setting: ' . $preview_file_id);
            } elseif (is_numeric($default_preview_setting)) {
                $preview_file_id = (int)$default_preview_setting;
                $this->utilities_service->debug_log('Preview URL: Using numeric setting as file ID: ' . $preview_file_id);
            } else {
                $this->utilities_service->debug_log('Preview URL: Could not parse default preview setting: ' . $default_preview_setting);
                return null;
            }
        }

        $this->utilities_service->debug_log('Preview URL: Using file ID: ' . $preview_file_id);

        // Get file info from EE Files
        $file_info = $this->_getFileInfo((int)$preview_file_id);
        
        if (!$file_info) {
            $this->utilities_service->debug_log('Preview URL: File not found for ID: ' . $preview_file_id);
            return null;
        }
        
        if (!$this->_isImageFile($file_info)) {
            $this->utilities_service->debug_log('Preview URL: File is not an image: ' . json_encode($file_info));
            return null;
        }

        $this->utilities_service->debug_log('Preview URL: Found valid image URL: ' . $file_info['url']);
        return $file_info['url'];
    }

    /**
     * Check if file is a valid image
     * 
     * @param array $file_info File information array
     * @return bool True if valid image file
     */
    private function _isImageFile(array $file_info): bool
    {
        $image_mime_types = [
            'image/jpeg',
            'image/jpg', 
            'image/png',
            'image/gif',
            'image/webp',
            'image/avif',
            'image/bmp'
        ];

        return in_array($file_info['mime_type'], $image_mime_types);
    }

    /**
     * Return JSON error response
     * 
     * @param string $message Error message
     * @return void
     */
    private function _returnJsonError(string $message): void
    {
        header('Content-Type: application/json');
        http_response_code(400);
        echo json_encode([
            'status' => 'error',
            'message' => $message
        ]);
        exit;
    }

    /**
     * Return JSON success response
     * 
     * @param array $data Response data
     * @return void
     */
    private function _returnJsonSuccess(array $data): void
    {
        header('Content-Type: application/json');
        echo json_encode([
            'status' => 'success',
            'data' => $data
        ]);
        exit;
    }

    /**
     * Validate and sanitize preview parameters
     * 
     * Uses ParameterRegistry for consistent validation with main processing pipeline.
     * 
     * @param array $parameters Raw parameters
     * @return array Validated parameters
     */
    private function _validatePreviewParameters(array $parameters): array
    {
        try {
            $validated = [];

            foreach ($parameters as $param_name => $param_value) {
                // Skip non-processing parameters
                if (in_array($param_name, ['src', 'action_link', 'url_only', 'act_what', 'act_path', 'cache'])) {
                    $validated[$param_name] = $param_value;
                    continue;
                }

                // Convert parameter name to lowercase for registry check
                $param_name_lower = strtolower($param_name);
                $canonical_name = ParameterRegistry::resolveParameterAlias($param_name_lower);
                
                // Validate using registry if parameter is known
                if (ParameterRegistry::parameterExists($param_name_lower)) {
                    // Sanitize dimensional parameters (width, height, max, etc.)
                    if (in_array($canonical_name, ['width', 'height', 'max', 'max_width', 'max_height', 'min', 'min_width', 'min_height'])) {
                        $sanitized_value = $this->validation_service->validate_dimension($param_value);
                        if ($sanitized_value !== false && $sanitized_value !== null) {
                            $validated[$canonical_name] = (string) $sanitized_value;
                        }
                    } else {
                        // For non-dimensional parameters, just use the value as-is
                        $validated[$canonical_name] = $param_value;
                    }
                }
            }

            return $validated;

        } catch (Exception $e) {
            $this->utilities_service->debug_log('Parameter validation error: ' . $e->getMessage());
            return $parameters; // Return original parameters as fallback
        }
    }
}
