<?php
/**
 * SCHEMAFY Field Generator Class
 * 
 * Dynamically generates form fields based on schema type configurations
 * 
 * @package CustomSchemaSchemafy
 * @since 2.0.0
 * @author SCHEMAFY Team
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Class SCHEMAFY_Field_Generator
 * 
 * Generates and validates form fields for schema builder
 */
class SCHEMAFY_Field_Generator {
    
    /**
     * Field configurations cache
     * 
     * @var array
     */
    private $field_configs = array();
    
    /**
     * Constructor
     */
    public function __construct() {
        // Configurations will be loaded on demand
    }
    
    /**
     * Emergency parent-type fallback map used if the shared registry cannot load.
     */
    private static $type_fallback = array(
        'NewsArticle'             => 'Article',
        'BlogPosting'             => 'Article',
        'AdvertiserContentArticle' => 'Article',
        'AnalysisNewsArticle'     => 'Article',
        'AskPublicNewsArticle'    => 'Article',
        'BackgroundNewsArticle'   => 'Article',
        'MedicalScholarlyArticle' => 'Article',
        'ReportageNewsArticle'    => 'Article',
        'TechArticle'             => 'Article',
        'SatiricalArticle'        => 'Article',
        'ScholarlyArticle'        => 'Article',
        'SocialMediaPosting'      => 'Article',
        'EventVenue'              => 'LocalBusiness',
        'Restaurant'              => 'LocalBusiness',
        'CafeOrCoffeeShop'        => 'LocalBusiness',
        'Dentist'                 => 'LocalBusiness',
        'Physician'               => 'LocalBusiness',
        'Physiotherapist'         => 'LocalBusiness',
        'Pharmacy'                => 'LocalBusiness',
        'BeautySalon'             => 'LocalBusiness',
        'HealthAndBeautyBusiness' => 'LocalBusiness',
        'Optician'                => 'LocalBusiness',
        'VeterinaryCare'          => 'LocalBusiness',
        'ChildCare'               => 'LocalBusiness',
        'ExerciseGym'             => 'LocalBusiness',
        'LegalService'            => 'LocalBusiness',
        'AccountingService'       => 'LocalBusiness',
        'FinancialService'        => 'LocalBusiness',
        'InsuranceAgency'         => 'LocalBusiness',
        'ProfessionalService'     => 'LocalBusiness',
        'RealEstateAgent'         => 'LocalBusiness',
        'LodgingBusiness'         => 'LocalBusiness',
        'AutoDealer'              => 'LocalBusiness',
        'AutoRepair'              => 'LocalBusiness',
        'AutoWash'                => 'LocalBusiness',
        'AutoRental'              => 'LocalBusiness',
        'AutomotiveBusiness'      => 'LocalBusiness',
        'TaxiService'             => 'LocalBusiness',
        'GovernmentOffice'        => 'LocalBusiness',
        'NailSalon'               => 'LocalBusiness',
        'TravelAgency'            => 'LocalBusiness',
        'Florist'                 => 'LocalBusiness',
        'MovingCompany'           => 'LocalBusiness',
        'Plumber'                 => 'LocalBusiness',
        'Electrician'             => 'LocalBusiness',
        'HVACBusiness'            => 'LocalBusiness',
        'GeneralContractor'       => 'LocalBusiness',
        'RoofingContractor'       => 'LocalBusiness',
        'HousePainter'            => 'LocalBusiness',
        'HomeAndConstructionBusiness' => 'LocalBusiness',
        'Locksmith'               => 'LocalBusiness',
        'FoodEstablishment'       => 'LocalBusiness',
        'Bakery'                  => 'LocalBusiness',
        'EntertainmentBusiness'   => 'LocalBusiness',
        'SportsActivityLocation'  => 'LocalBusiness',
        'MedicalBusiness'         => 'LocalBusiness',
        'Airline'                 => 'Organization',
        'Consortium'              => 'Organization',
        'Corporation'             => 'Organization',
        'EducationalOrganization' => 'Organization',
        'GovernmentOrganization'  => 'Organization',
        'LibrarySystem'           => 'Organization',
        'NGO'                     => 'Organization',
        'NewsMediaOrganization'   => 'Organization',
        'OnlineBusiness'          => 'Organization',
        'PerformingGroup'         => 'Organization',
        'Project'                 => 'Organization',
        'ResearchOrganization'    => 'Organization',
        'SearchRescueOrganization' => 'Organization',
        'SportsOrganization'      => 'Organization',
        'WorkersUnion'            => 'Organization',
        'AboutPage'               => 'WebPage',
        'CheckoutPage'            => 'WebPage',
        'ContactPage'             => 'WebPage',
        'WebSite'                 => 'WebPage',
        'ItemPage'                => 'WebPage',
        'MedicalWebPage'          => 'WebPage',
        'ProfilePage'             => 'WebPage',
        'QAPage'                  => 'WebPage',
        'RealEstateListing'       => 'WebPage',
        'SearchResultsPage'       => 'WebPage',
        'CollectionPage'          => 'WebPage',
        'ImageGallery'            => 'WebPage',
    );

    /**
     * Get field configuration for a schema type.
     * Automatically falls back to the parent type's config when no specific JSON exists.
     *
     * @param string $schema_type Schema type (Article, Product, EventVenue, etc.)
     * @return array|null Field configuration or null if not found
     */
    public function get_field_config($schema_type) {
        // Check cache first
        if (isset($this->field_configs[$schema_type])) {
            return $this->field_configs[$schema_type];
        }

        $config = $this->load_config_file($schema_type);
        $fallback_schema_type = self::get_type_fallback($schema_type);

        if ($config === null && $fallback_schema_type) {
            $config = $this->load_config_file($fallback_schema_type);
        }

        if ($config === null) {
            return null;
        }

        // Translate labels/descriptions before caching
        $config = $this->translate_config($config);

        // Cache under the requested type (not the fallback)
        $this->field_configs[$schema_type] = $config;

        return $config;
    }

    /**
     * What each schema type actually requires, for the JSON validators.
     *
     * Derived from the very field configs the Smart Builder is generated from,
     * so there is one description of "an Article needs a headline" in the
     * plugin instead of a second list hand-maintained in JavaScript. Types
     * whose config is absent — every paid type in the free package — simply
     * produce no rules, and the validators then check only structure for them
     * rather than inventing requirements they cannot know.
     *
     * @return array[] schemaType => [ required => string[], maxLength => array ]
     */
    public static function get_validation_rules() {
        static $rules = null;

        if (null !== $rules) {
            return $rules;
        }

        $rules = array();
        $configs = glob(SCHEMAFY_PLUGIN_PATH . 'templates/builder/field-configs/*-schema*.json');

        foreach ($configs ?: array() as $config_file) {
            $config = json_decode(file_get_contents($config_file), true);

            if (!is_array($config) || empty($config['schemaType']) || empty($config['fields']) || !is_array($config['fields'])) {
                continue;
            }

            $required = array();
            $max_length = array();

            foreach ($config['fields'] as $field) {
                if (!is_array($field)) {
                    continue;
                }

                // schemaPath is where the value lands in the JSON-LD; name is
                // only the form control's name, so it is the weaker fallback.
                $path = '';
                if (!empty($field['schemaPath'])) {
                    $path = (string) $field['schemaPath'];
                } elseif (!empty($field['name'])) {
                    $path = (string) $field['name'];
                }

                if ('' === $path) {
                    continue;
                }

                if (!empty($field['required'])) {
                    $required[] = $path;
                }

                if (!empty($field['maxLength'])) {
                    $max_length[$path] = (int) $field['maxLength'];
                }
            }

            $rules[(string) $config['schemaType']] = array(
                'required'  => array_values(array_unique($required)),
                'maxLength' => $max_length,
            );
        }

        return $rules;
    }

    /**
     * Load and parse a single JSON config file.
     *
     * @param string $schema_type
     * @return array|null
     */
    private function load_config_file($schema_type) {
        $base = SCHEMAFY_PLUGIN_PATH . 'templates/builder/field-configs/' . strtolower($schema_type) . '-schema';
        $config_file = $base . '.json';

        // Premium schema types ship as *-schema__premium_only.json, which
        // Freemius strips from the free WordPress.org build. In the premium
        // build only the __premium_only variant exists for those types; in the
        // free build neither exists, so premium types resolve to null.
        if (!file_exists($config_file)) {
            $premium_file = $base . '__premium_only.json';
            if (!file_exists($premium_file)) {
                return null;
            }
            $config_file = $premium_file;
        }

        $config_json = file_get_contents($config_file);
        $config = json_decode($config_json, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            schemafy_log('[SCHEMAFY] Invalid JSON in config file: ' . $config_file);
            return null;
        }

        return $config;
    }

    /**
     * Resolve the fallback config type for a Schema.org subtype.
     *
     * Public so optional modules can resolve a subtype to its parent without
     * duplicating the map.
     *
     * @param string $schema_type Requested schema type.
     * @return string|null Fallback type or null when none exists.
     */
    public static function get_type_fallback($schema_type) {
        if (class_exists('SCHEMAFY_Schema_Type_Registry')) {
            $fallback_map = SCHEMAFY_Schema_Type_Registry::get_fallback_map();

            if (isset($fallback_map[$schema_type])) {
                return $fallback_map[$schema_type];
            }
        }

        return isset(self::$type_fallback[$schema_type]) ? self::$type_fallback[$schema_type] : null;
    }
    
    /**
     * Translate a single string that comes from a JSON field-config file.
     *
     * Field labels/descriptions live in external JSON, so they are dynamic at
     * call time. The i18n template tags (__(), _e(), …) require a string
     * literal so the .pot scanner can extract them, and Plugin Check rejects
     * __( $variable ). We instead resolve the value against the already-loaded
     * "schemafy" textdomain via the low-level Translations object — so labels
     * still translate from whatever catalog WordPress loaded (translate.wordpress.org
     * ships those) without a flagged template tag. Strings unknown to the
     * catalog are returned unchanged.
     *
     * @param mixed $text Candidate string.
     * @return mixed Translated string (or the original value untouched).
     */
    private function translate_dynamic( $text ) {
        if ( ! is_string( $text ) || '' === $text ) {
            return $text;
        }
        return get_translations_for_domain( 'schemafy' )->translate( $text );
    }

    /**
     * Recursively translate label, description, and helpText fields in a schema config.
     */
    private function translate_config( array $config ): array {
        if ( isset( $config['description'] ) ) {
            $config['description'] = $this->translate_dynamic( $config['description'] );
        }
        if ( isset( $config['fields'] ) && is_array( $config['fields'] ) ) {
            $config['fields'] = array_map( [ $this, 'translate_field_entry' ], $config['fields'] );
        }
        return $config;
    }

    /**
     * Translate a single field entry (and recurse into sub-fields).
     */
    private function translate_field_entry( array $field ): array {
        if ( isset( $field['description'] ) ) {
            $field['description'] = $this->translate_dynamic( $field['description'] );
        }
        if ( isset( $field['label'] ) ) {
            $field['label'] = $this->translate_dynamic( $field['label'] );
        }
        if ( isset( $field['placeholder'] ) ) {
            $field['placeholder'] = $this->translate_dynamic( $field['placeholder'] );
        }
        if ( isset( $field['helpText'] ) ) {
            $field['helpText'] = $this->translate_dynamic( $field['helpText'] );
        }
        if ( isset( $field['options'] ) && is_array( $field['options'] ) ) {
            $field['options'] = array_map(
                function( $option ) {
                    if ( is_array( $option ) ) {
                        if ( isset( $option['label'] ) ) {
                            $option['label'] = $this->translate_dynamic( $option['label'] );
                        }
                        if ( isset( $option['description'] ) ) {
                            $option['description'] = $this->translate_dynamic( $option['description'] );
                        }
                    }
                    return $option;
                },
                $field['options']
            );
        }
        if ( isset( $field['fields'] ) && is_array( $field['fields'] ) ) {
            $field['fields'] = array_map( [ $this, 'translate_field_entry' ], $field['fields'] );
        }
        return $field;
    }

    /**
     * Render form field based on configuration
     *
     * @param array $field_config Field configuration
     * @param mixed $value Current value
     * @return string HTML output
     */
    public function render_field($field_config, $value = '') {
        $type = $field_config['type'] ?? 'text';
        $name = $field_config['name'] ?? '';
        $label = $field_config['label'] ?? '';
        $required = $field_config['required'] ?? false;
        $placeholder = $field_config['placeholder'] ?? '';
        
        $html = '<div class="schemafy-field-group" data-field-name="' . esc_attr($name) . '">';
        
        // Label
        $html .= '<label class="schemafy-field-label">';
        $html .= esc_html($label);
        if ($required) {
            $html .= ' <span class="schemafy-required">*</span>';
        }
        $html .= '</label>';
        
        // Field based on type
        switch ($type) {
            case 'text':
                $html .= $this->render_text_field($field_config, $value);
                break;
            
            case 'textarea':
                $html .= $this->render_textarea_field($field_config, $value);
                break;
            
            case 'select':
                $html .= $this->render_select_field($field_config, $value);
                break;
            
            case 'number':
                $html .= $this->render_number_field($field_config, $value);
                break;
            
            case 'url':
                $html .= $this->render_url_field($field_config, $value);
                break;
            
            case 'datetime':
                $html .= $this->render_datetime_field($field_config, $value);
                break;
            
            case 'image':
                $html .= $this->render_image_field($field_config, $value);
                break;
            
            case 'group':
                $html .= $this->render_group_field($field_config, $value);
                break;
            
            case 'repeater':
                $html .= $this->render_repeater_field($field_config, $value);
                break;
            
            default:
                $html .= $this->render_text_field($field_config, $value);
        }
        
        // Character counter (if applicable)
        if (isset($field_config['maxLength'])) {
            $current_length = is_string($value) ? strlen($value) : 0;
            $max_length = $field_config['maxLength'];
            $html .= '<div class="schemafy-char-counter">';
            $html .= '<span class="current">' . $current_length . '</span> / ';
            $html .= '<span class="max">' . $max_length . '</span>';
            $html .= '</div>';
        }
        
        // Validation message container
        $html .= '<div class="schemafy-field-validation"></div>';
        
        $html .= '</div>';
        
        return $html;
    }
    
    /**
     * Render text input field
     */
    private function render_text_field($config, $value) {
        $name = $config['name'] ?? '';
        $placeholder = $config['placeholder'] ?? '';
        $maxlength = $config['maxLength'] ?? '';
        $required = $config['required'] ?? false;
        
        $html = '<input type="text" ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-input" ';
        $html .= 'value="' . esc_attr($value) . '" ';
        $html .= 'placeholder="' . esc_attr($placeholder) . '" ';
        if ($maxlength) {
            $html .= 'maxlength="' . esc_attr($maxlength) . '" ';
        }
        if ($required) {
            $html .= 'required ';
        }
        $html .= '/>';
        
        return $html;
    }
    
    /**
     * Render textarea field
     */
    private function render_textarea_field($config, $value) {
        $name = $config['name'] ?? '';
        $placeholder = $config['placeholder'] ?? '';
        $rows = $config['rows'] ?? 4;
        $maxlength = $config['maxLength'] ?? '';
        $required = $config['required'] ?? false;
        
        $html = '<textarea ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-textarea" ';
        $html .= 'rows="' . esc_attr($rows) . '" ';
        $html .= 'placeholder="' . esc_attr($placeholder) . '" ';
        if ($maxlength) {
            $html .= 'maxlength="' . esc_attr($maxlength) . '" ';
        }
        if ($required) {
            $html .= 'required ';
        }
        $html .= '>' . esc_textarea($value) . '</textarea>';
        
        return $html;
    }
    
    /**
     * Render select dropdown field
     */
    private function render_select_field($config, $value) {
        $name = $config['name'] ?? '';
        $options = $config['options'] ?? array();
        $required = $config['required'] ?? false;
        
        $html = '<select ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-select" ';
        if ($required) {
            $html .= 'required ';
        }
        $html .= '>';
        
        $html .= '<option value="">-- Select --</option>';
        
        foreach ($options as $option) {
            $selected = ($value === $option) ? 'selected' : '';
            $html .= '<option value="' . esc_attr($option) . '" ' . $selected . '>';
            $html .= esc_html($option);
            $html .= '</option>';
        }
        
        $html .= '</select>';
        
        return $html;
    }
    
    /**
     * Render number input field
     */
    private function render_number_field($config, $value) {
        $name = $config['name'] ?? '';
        $placeholder = $config['placeholder'] ?? '';
        $min = $config['min'] ?? '';
        $max = $config['max'] ?? '';
        $step = $config['step'] ?? '1';
        $required = $config['required'] ?? false;
        
        $html = '<input type="number" ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-input schemafy-field-number" ';
        $html .= 'value="' . esc_attr($value) . '" ';
        $html .= 'placeholder="' . esc_attr($placeholder) . '" ';
        $html .= 'step="' . esc_attr($step) . '" ';
        if ($min !== '') {
            $html .= 'min="' . esc_attr($min) . '" ';
        }
        if ($max !== '') {
            $html .= 'max="' . esc_attr($max) . '" ';
        }
        if ($required) {
            $html .= 'required ';
        }
        $html .= '/>';
        
        return $html;
    }
    
    /**
     * Render URL input field
     */
    private function render_url_field($config, $value) {
        $name = $config['name'] ?? '';
        $placeholder = $config['placeholder'] ?? '';
        $required = $config['required'] ?? false;
        
        $html = '<input type="url" ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-input schemafy-field-url" ';
        $html .= 'value="' . esc_attr($value) . '" ';
        $html .= 'placeholder="' . esc_attr($placeholder) . '" ';
        if ($required) {
            $html .= 'required ';
        }
        $html .= '/>';
        
        return $html;
    }
    
    /**
     * Render datetime picker field
     */
    private function render_datetime_field($config, $value) {
        $name = $config['name'] ?? '';
        $required = $config['required'] ?? false;
        
        $html = '<input type="datetime-local" ';
        $html .= 'name="' . esc_attr($name) . '" ';
        $html .= 'class="schemafy-field-input schemafy-field-datetime" ';
        $html .= 'value="' . esc_attr($value) . '" ';
        if ($required) {
            $html .= 'required ';
        }
        $html .= '/>';
        
        return $html;
    }
    
    /**
     * Render image upload field (WordPress media library)
     */
    private function render_image_field($config, $value) {
        $name = $config['name'] ?? '';
        $required = $config['required'] ?? false;
        
        $html = '<div class="schemafy-image-field">';
        $html .= '<input type="hidden" name="' . esc_attr($name) . '" class="schemafy-image-value" value="' . esc_attr($value) . '" />';
        
        if ($value) {
            $html .= '<div class="schemafy-image-preview">';
            $html .= '<img src="' . esc_url($value) . '" />';
            $html .= '<button type="button" class="schemafy-remove-image">✕</button>';
            $html .= '</div>';
        }
        
        $html .= '<button type="button" class="schemafy-btn schemafy-upload-image">';
        $html .= __('Upload Image', 'schemafy');
        $html .= '</button>';
        $html .= '</div>';
        
        return $html;
    }
    
    /**
     * Render group field (nested fields)
     */
    private function render_group_field($config, $value) {
        $sub_fields = $config['fields'] ?? array();
        
        $html = '<div class="schemafy-field-group-container">';
        
        foreach ($sub_fields as $sub_field) {
            $sub_value = is_array($value) && isset($value[$sub_field['name']]) ? $value[$sub_field['name']] : '';
            $html .= $this->render_field($sub_field, $sub_value);
        }
        
        $html .= '</div>';
        
        return $html;
    }
    
    /**
     * Render repeater field (add/remove multiple sets)
     */
    private function render_repeater_field($config, $value) {
        $name = $config['name'] ?? '';
        $sub_fields = $config['fields'] ?? array();
        $values = is_array($value) ? $value : array();
        
        $html = '<div class="schemafy-repeater-field" data-field-name="' . esc_attr($name) . '">';
        $html .= '<div class="schemafy-repeater-items">';
        
        // Render existing items
        if (!empty($values)) {
            foreach ($values as $index => $item_value) {
                $html .= $this->render_repeater_item($sub_fields, $item_value, $index);
            }
        }
        
        $html .= '</div>';
        $html .= '<button type="button" class="schemafy-btn schemafy-repeater-add">';
        $html .= __('Add Item', 'schemafy');
        $html .= '</button>';
        $html .= '</div>';
        
        // Template for new items (hidden)
        $html .= '<script type="text/template" class="schemafy-repeater-template">';
        $html .= $this->render_repeater_item($sub_fields, array(), '__INDEX__');
        $html .= '</script>';
        
        return $html;
    }
    
    /**
     * Render single repeater item
     */
    private function render_repeater_item($sub_fields, $value, $index) {
        $html = '<div class="schemafy-repeater-item" data-index="' . esc_attr($index) . '">';
        
        foreach ($sub_fields as $sub_field) {
            $sub_value = is_array($value) && isset($value[$sub_field['name']]) ? $value[$sub_field['name']] : '';
            $html .= $this->render_field($sub_field, $sub_value);
        }
        
        $html .= '<button type="button" class="schemafy-repeater-remove">Remove</button>';
        $html .= '</div>';
        
        return $html;
    }
    
    /**
     * Validate field value against configuration
     * 
     * @param mixed $value Field value
     * @param array $config Field configuration
     * @return array Validation result
     */
    public function validate_field($value, $config) {
        $errors = array();
        
        // Required check
        if (($config['required'] ?? false) && empty($value)) {
            $errors[] = __('This field is required', 'schemafy');
        }
        
        // Type-specific validation
        if (!empty($value) && isset($config['validation'])) {
            $validation = $config['validation'];
            
            // Min length
            if (isset($validation['minLength']) && strlen($value) < $validation['minLength']) {
                $errors[] = sprintf(
                    // translators: %d is a value inserted at runtime.
                    __('Minimum length is %d characters', 'schemafy'),
                    $validation['minLength']
                );
            }
            
            // Max length
            if (isset($config['maxLength']) && strlen($value) > $config['maxLength']) {
                $errors[] = sprintf(
                    // translators: %d is a value inserted at runtime.
                    __('Maximum length is %d characters', 'schemafy'),
                    $config['maxLength']
                );
            }
            
            // Pattern matching
            if (isset($validation['pattern']) && !empty($validation['pattern'])) {
                if (!preg_match($validation['pattern'], $value)) {
                    $errors[] = __('Invalid format', 'schemafy');
                }
            }
        }
        
        return array(
            'valid' => empty($errors),
            'errors' => $errors
        );
    }
    
    /**
     * Get template data for a schema type
     * 
     * @param string $schema_type Schema type
     * @param string $template_name Template name
     * @return array|null Template data or null
     */
    public function get_template($schema_type, $template_name) {
        $config = $this->get_field_config($schema_type);
        
        if (!$config || !isset($config['templates'])) {
            return null;
        }
        
        foreach ($config['templates'] as $template) {
            if ($template['name'] === $template_name) {
                return $template;
            }
        }
        
        return null;
    }
}
