<?php

namespace GriffinForms\Admin\Html\FormControls\Field\FieldSettings;

class FileUploadSettings extends \GriffinForms\Admin\Html\FormControls\Field\FieldSettings\Format
{
    protected $allow_multiple_select; // bool: controls <input type="file" multiple>
    protected $mime_quick_select;
    protected $allowed_mimes;
    protected $max_size_mb;
    protected $total_size_mb;
    protected $max_files;
    protected $storage_mode;
    protected $custom_mimes;
    
    // Image constraints
    protected $image_min_width;
    protected $image_max_width;
    protected $image_min_height;
    protected $image_max_height;
    protected $image_max_megapixels;
    protected $image_aspect_mode;       // free|preset|custom
    protected $image_aspect_preset;     // 1:1,3:2,4:3,16:9
    protected $image_aspect_custom_w;   // int
    protected $image_aspect_custom_h;   // int
    protected $image_auto_rotate_exif;  // bool
    protected $image_strip_exif;        // bool
    
    protected function setMimeQuickSelect()
    {
        $this->mime_quick_select = isset($this->value['mime_quick_select']) ? $this->value['mime_quick_select'] : 'all';
    }

    protected function setAllowedMimes()
    {
        $this->allowed_mimes = isset($this->value['allowed_mimes']) ? $this->value['allowed_mimes'] : '';
    }

    protected function setMaxSizeMb()
    {
        $this->max_size_mb = isset($this->value['max_size_mb']) ? $this->value['max_size_mb'] : '';
    }

    protected function setTotalSizeMb()
    {
        $this->total_size_mb = isset($this->value['total_size_mb']) ? $this->value['total_size_mb'] : '';
    }

    /**
     * Total upload size cap across all files (in MB).
     * Complements per-file cap. Leave blank for no total cap.
     */
    protected function totalSizeMb()
    {
        $label = $this->lang->getText('total_size_mb', 'label');
        $desc  = $this->lang->getText('total_size_mb', 'description');
        $value = is_numeric($this->total_size_mb) ? $this->total_size_mb : '';
        $id    = 'gf-fileupload-total-size-mb';

        echo '<div class="mb-3">';
        echo '<label for="' . esc_attr( $id ) . '" class="form-label">' . esc_html( $label ) . '</label>';
        echo '<input type="text" inputmode="decimal" pattern="^\\d+(?:\\.\\d+)?$" class="form-control" style="min-width:150px !important; max-width:150px" id="' . esc_attr( $id ) . '" name="total_size_mb" value="' . esc_attr( $value ) . '">';
        echo '<div class="form-text">' . esc_html( $desc ) . '</div>';
        echo '</div>';
    }

    protected function setMaxFiles()
    {
        $this->max_files = isset($this->value['max_files']) ? $this->value['max_files'] : '';
    }

    protected function setAllowMultipleSelect()
    {
        $this->allow_multiple_select = !empty($this->value['allow_multiple_select']);
    }

    protected function setStorageMode()
    {
        // Respect explicit field value if present
        if (isset($this->value['storage_mode']) && $this->value['storage_mode'] !== '') {
            $this->storage_mode = (string) $this->value['storage_mode'];
            return;
        }

        // Otherwise, use global Files Storage default from settings
        $default = 'private';
        if (isset($this->settings) && is_object($this->settings) && method_exists($this->settings, 'getOption')) {
            $opt = $this->settings->getOption('files_default_storage_mode');
            if (is_string($opt) && $opt !== '') {
                $opt = strtolower(trim($opt));
                if (in_array($opt, array('private', 'media_library'), true)) {
                    $default = $opt;
                }
            }
        }

        $this->storage_mode = $default;
    }

    protected function setCustomMimes()
    {
        $this->custom_mimes = isset($this->value['custom_mimes']) ? $this->value['custom_mimes'] : '';
    }

    protected function setImageConstraints()
    {
        $v = is_array($this->value) ? $this->value : array();

        $int_or_blank = static function ($arr, $key) {
            if (!array_key_exists($key, $arr)) { return ''; }
            $raw = $arr[$key];
            if ($raw === '' || $raw === null) { return ''; }
            return (is_numeric($raw) && (int)$raw > 0) ? (int)$raw : '';
        };

        $float_or_blank = static function ($arr, $key) {
            if (!array_key_exists($key, $arr)) { return ''; }
            $raw = $arr[$key];
            if ($raw === '' || $raw === null) { return ''; }
            return (is_numeric($raw) && (float)$raw > 0) ? (float)$raw : '';
        };

        $this->image_min_width       = $int_or_blank($v, 'image_min_width');
        $this->image_max_width       = $int_or_blank($v, 'image_max_width');
        $this->image_min_height      = $int_or_blank($v, 'image_min_height');
        $this->image_max_height      = $int_or_blank($v, 'image_max_height');
        $this->image_max_megapixels  = $float_or_blank($v, 'image_max_megapixels');

        $this->image_aspect_mode     = isset($v['image_aspect_mode']) ? (string) $v['image_aspect_mode'] : 'free';
        $this->image_aspect_preset   = isset($v['image_aspect_preset']) ? (string) $v['image_aspect_preset'] : '4:3';
        $this->image_aspect_custom_w = $int_or_blank($v, 'image_aspect_custom_w');
        $this->image_aspect_custom_h = $int_or_blank($v, 'image_aspect_custom_h');
        $this->image_auto_rotate_exif= !empty($v['image_auto_rotate_exif']);
        $this->image_strip_exif      = !empty($v['image_strip_exif']);
    }

    public function html()
    {
        $this->setAllowedMimes();
        $this->setCustomMimes();
        $this->setTotalSizeMb();
        $this->setMimeQuickSelect();
        $this->allowedMimes();
        $this->maxSizeMb();
        $this->totalSizeMb();
        $this->setImageConstraints();
        $this->imageConstraints();
        $this->maxFiles();
        $this->setAllowMultipleSelect();
        $this->allowMultipleSelect();
        $this->storageMode();
    }

    /**
     * Toggle for the HTML multiple attribute on the frontend file input.
     * When enabled, users can select multiple files in a single picker dialog.
     */
    protected function allowMultipleSelect()
    {
        $id    = 'gf-fileupload-allow-multiple';
        $label = $this->lang->getText('allow_multiple_select', 'label');
        $desc  = $this->lang->getText('allow_multiple_select', 'description');

        echo '<div class="mb-3">';
        echo '  <div class="">';
        echo '    <input type="checkbox" class="" id="' . esc_attr($id) . '" name="allow_multiple_select" value="1" ' . checked(true, (bool) $this->allow_multiple_select, false) . ' />';
        echo '    <label class="align-bottom" for="' . esc_attr($id) . '">' . esc_html($label) . '</label>';
        echo '  </div>';
        echo '  <div class="form-text">' . esc_html($desc) . '</div>';
        echo '</div>';
    }

    /**
     * Render Quick Select dropdown for MIME categories using choices provider.
     */
    protected function mimeQuickSelectDropdown()
    {
        $choices  = (array) $this->choices->getChoices('mime_quick_select');
        $current  = is_string($this->mime_quick_select) ? $this->mime_quick_select : '';

        echo '<div class="mb-2 d-flex align-items-center gap-2">';
        echo   '<select id="gf-mime-quick-select" name="mime_quick_select" class="form-select w-auto">';
        
        foreach ( $choices as $value => $label ) {
            // Support both associative (value => label) and indexed arrays
            $val = (is_string($value) && $value !== '') ? $value : ( (is_string($label) && $label !== '') ? $label : '' );
            if ($val === '') { continue; }
            $text = (is_string($label) && $label !== '') ? $label : ucfirst($val);

            echo '<option value="' . esc_attr($val) . '" ' . selected($current, $val, false) . '>' . esc_html($text) . '</option>';
        }

        echo   '</select>';
        echo '</div>';
    }

    protected function allowedMimes()
    {
        // Fetch preset arrays per family from choices class. These keys
        // will be populated in the Choices provider next.
        // Each call MUST return an array of extension presets (e.g., [ 'jpg', 'jpeg', 'png' ]).
        $image_mimes        = (array) $this->choices->getChoices('image_mimes');
        $document_mimes     = (array) $this->choices->getChoices('document_mimes');
        $spreadsheet_mimes  = (array) $this->choices->getChoices('spreadsheet_mimes');
        $presentation_mimes = (array) $this->choices->getChoices('presentation_mimes');
        $pdf_mimes          = (array) $this->choices->getChoices('pdf_mimes');
        $audio_mimes        = (array) $this->choices->getChoices('audio_mimes');
        $video_mimes        = (array) $this->choices->getChoices('video_mimes');
        $archive_mimes      = (array) $this->choices->getChoices('archive_mimes');
        $code_mimes         = (array) $this->choices->getChoices('code_mimes');
        $text_mimes         = (array) $this->choices->getChoices('text_mimes');
        $other_mimes        = (array) $this->choices->getChoices('other_mimes');

        // Normalized container for later rendering (checkbox grids with select-all per family).
        // Keeping local for now; rendering will be added in the next step.
        $mime_groups = array(
            'image'        => $image_mimes,
            'document'     => $document_mimes,
            'spreadsheet'  => $spreadsheet_mimes,
            'presentation' => $presentation_mimes,
            'pdf'          => $pdf_mimes,
            'audio'        => $audio_mimes,
            'video'        => $video_mimes,
            'archive'      => $archive_mimes,
            'code'         => $code_mimes,
            'text'         => $text_mimes,
            'other'        => $other_mimes,
        );

        // Render containers for each group
        $titles = array(
            'image'        => __( 'Images', 'griffinforms-form-builder' ),
            'document'     => __( 'Documents', 'griffinforms-form-builder' ),
            'spreadsheet'  => __( 'Spreadsheets', 'griffinforms-form-builder' ),
            'presentation' => __( 'Presentations', 'griffinforms-form-builder' ),
            'pdf'          => __( 'PDF', 'griffinforms-form-builder' ),
            'audio'        => __( 'Audio', 'griffinforms-form-builder' ),
            'video'        => __( 'Video', 'griffinforms-form-builder' ),
            'archive'      => __( 'Archives', 'griffinforms-form-builder' ),
            'code'         => __( 'Code', 'griffinforms-form-builder' ),
            'text'         => __( 'Text', 'griffinforms-form-builder' ),
            'other'        => __( 'Other', 'griffinforms-form-builder' ),
            'svg_unsafe'   => __( 'SVG (Unsafe — opt‑in)', 'griffinforms-form-builder' ),
        );

        $label = $this->lang->getText('allowed_mimes', 'label');
        $id = 'gf-fileupload-allowed-mimes';

        echo '<div class="mb-3">';
        echo '<label for="' . esc_attr($id) . '" class="form-label">' . esc_html($label) . '</label>';
        $this->mimeQuickSelectDropdown();
        foreach ( $mime_groups as $group_key => $items ) {
            $title = isset( $titles[ $group_key ] ) ? $titles[ $group_key ] : ucfirst( (string) $group_key );
            $this->renderMimeContainer( $group_key, $title, (array) $items );
        }
        $this->renderCustomMimesInput();
        echo '</div>';

        // TODO (GRIFF-185): Use $mime_groups to render grouped checkbox UI and bind to
        // $this->allowed_mimes (persisting extensions), plus computed accept preview.
    }

    /**
     * Render a reusable container for a MIME family section.
     * Prints a Bootstrap card with a heading and an empty grid body where
     * checkboxes will be inserted in a later step.
     *
     * @param string $group_key Unique key for the group (e.g., 'image', 'audio').
     * @param string $title     Visible heading for the group.
     * @param array  $items     List of extensions for this group (used to show count only for now).
     */
    protected function renderMimeContainer( $group_key, $title, array $items = array() )
    {
        $group_key = (string) $group_key;
        $title     = (string) $title;
        $count     = is_array( $items ) ? count( $items ) : 0;

        $label_id = 'gf-mime-' . sanitize_html_class( $group_key ) . '-label';

        echo '<div class="border bg-white rounded mb-3 gf-mime-container overflow-hidden" data-gf-mime-group="' . esc_attr( $group_key ) . '">';
        echo   '<div class="p-2 border-bottom d-flex align-items-center justify-content-start">';
        echo     '<span id="' . esc_attr( $label_id ) . '" class="gf-mime-title fw-semibold">' . esc_html( $title ) . '</span>'; 
        echo     '<span class="badge text-bg-secondary gf-mime-count ms-1">' . intval( $count ) . '</span>';
        echo   '</div>';
        echo   '<div class="p-3">';
        if ($group_key === 'code') {
            $this->renderCodeMimeNotice();
        }
        echo     '<div class="row row-cols-2 row-cols-md-3 row-cols-lg-4 gy-2 gf-mime-grid" role="group" aria-labelledby="' . esc_attr( $label_id ) . '">';
        
        // Render checkboxes for each extension in this group
        $selected = $this->allowed_mimes;
        if ( ! is_array( $selected ) ) {
            // Accept JSON or comma-separated strings just in case
            if ( is_string( $selected ) && $selected !== '' ) {
                $decoded = json_decode( $selected, true );
                if ( is_array( $decoded ) ) {
                    $selected = $decoded;
                } else {
                    $selected = array_map( 'trim', explode( ',', $selected ) );
                }
            } else {
                $selected = array();
            }
        }
        // Normalize selected to lowercase strings
        $selected = array_filter( array_map( static function( $v ) {
            return is_string( $v ) ? strtolower( $v ) : '';
        }, $selected ) );

        foreach ( (array) $items as $ext_key => $ui_label ) {
            // Support both associative ( 'pdf' => 'PDF' ) and indexed ( [ 'pdf', 'jpg' ] ) arrays
            if ( is_string( $ext_key ) && $ext_key !== '' ) {
                $ext = strtolower( $ext_key );
            } elseif ( is_string( $ui_label ) && $ui_label !== '' ) {
                // Fallback when an indexed array of extensions is provided
                $ext = strtolower( $ui_label );
            } else {
                continue;
            }

            // sanitize_key keeps [a-z0-9_] only; extensions are safe under this
            $ext_slug  = sanitize_key( $ext );
            $input_id  = 'gf-mime-' . sanitize_html_class( $group_key ) . '-' . $ext_slug;
            $is_checked = in_array( $ext, $selected, true );

            // Prefer provided UI label; fallback to uppercase ext
            $label_text = ( is_string( $ui_label ) && $ui_label !== '' && ! is_numeric( $ext_key ) ) ? $ui_label : strtoupper( $ext );

            echo '<div class="col">';
            echo   '<label class="gf-wp-checkbox d-inline-flex align-items-center gap-1" for="' . esc_attr( $input_id ) . '">';
            echo     '<input type="checkbox" class="m-0 me-1" id="' . esc_attr( $input_id ) . '" name="allowed_mimes[]" value="' . esc_attr( $ext ) . '" ' . checked( $is_checked, true, false ) . ' />';
            echo     '<span>' . esc_html( $label_text ) . '</span>';
            echo   '</label>';
            echo '</div>';
        }
        echo     '</div>';
        echo   '</div>';
        echo '</div>';
    }

    protected function getPhpUploadLimit()
    {
        $bytes = function_exists('wp_max_upload_size') ? (int) wp_max_upload_size() : 0;
        $mb    = $bytes > 0 ? (int) floor($bytes / MB_IN_BYTES) : 0;
        $human = $bytes > 0 ? size_format($bytes) : '';

        return array(
            'bytes' => $bytes,
            'mb'    => $mb,
            'human' => $human,
        );
    }

    protected function maxSizeMb()
    {
        $label = $this->lang->getText('max_size_mb', 'label');
        $desc  = $this->lang->getText('max_size_mb', 'description');
        $value = is_numeric($this->max_size_mb) ? $this->max_size_mb : '';
        $id    = 'gf-fileupload-max-size-mb';

        $limit = $this->getPhpUploadLimit();
        $php_max_mb      = isset($limit['mb']) ? (int) $limit['mb'] : 0;
        $server_limit_hr = isset($limit['human']) ? $limit['human'] : '';

        // Append server limit hint to description if available
        if ( $server_limit_hr ) {
            /* translators: %s: server max upload size (e.g., 128 MB) */
            $desc .= ' ' . sprintf( __( 'Server limit: %s per file.', 'griffinforms-form-builder' ), $server_limit_hr );
        }

        echo '<div class="mb-3">';
        echo '<label for="' . esc_attr( $id ) . '" class="form-label">' . esc_html( $label ) . '</label>';
        echo '<input type="text" inputmode="decimal" pattern="^\\d+(?:\\.\\d+)?$" class="form-control" style="min-width:150px !important; max-width:150px" id="' . esc_attr( $id ) . '" name="max_size_mb" value="' . esc_attr( $value ) . '">';
        echo '<div class="form-text">' . esc_html( $desc ) . '</div>';
        echo '</div>';
    }

    protected function maxFiles()
    {
        $label = $this->lang->getText('max_files', 'label');
        $desc  = $this->lang->getText('max_files', 'description');
        $value = is_numeric($this->max_files) ? $this->max_files : '';
        $id    = 'gf-fileupload-max-files';

        echo '<div class="mb-3">';
        echo '<label for="' . esc_attr($id) . '" class="form-label">' . esc_html($label) . '</label>';
        echo '<input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control" style="min-width:150px !important; max-width:150px" id="' . esc_attr($id) . '" name="max_files" value="' . esc_attr($value) . '">';
        echo '<div class="form-text">' . esc_html($desc) . '</div>';
        echo '</div>';
    }

    protected function storageMode()
    {
        $label = $this->lang->getText('storage_mode', 'label');
        $desc  = $this->lang->getText('storage_mode', 'description');
        $options = $this->choices->getChoices('storage_mode');
        $field = new \GriffinForms\Admin\Html\FormControls\SelectField($this->name . '_storage_mode', $this->storage_mode);
        $field->selectOptions($options);
        $id = $this->name . '_storage_mode';

        echo '<div class="mb-3">';
        echo '<label for="' . esc_attr($id) . '" class="form-label">' . esc_html($label) . '</label>';
        echo '<div>';
        $field->html();
        echo '</div>';
        echo '<div class="form-text">' . esc_html($desc) . '</div>';
        echo '</div>';
    }

        /**
     * Render a free-form input for advanced users to add custom extensions or MIME strings.
     * Stored as a comma-separated string in `custom_mimes`. Server-side will normalize and validate.
     */
    protected function renderCustomMimesInput()
    {
        $label = __( 'Custom file types (advanced)', 'griffinforms-form-builder' );
        $desc  = __( 'Comma-separated extensions or MIME types. Examples: jpg, heic, application/vnd.ms-excel. WordPress will still enforce its allowlist unless you extend it via upload_mimes.', 'griffinforms-form-builder' );
        $id    = 'gf-fileupload-custom-mimes';

        // Accept array or string; render as CSV
        $value = $this->custom_mimes;
        if ( is_array( $value ) ) {
            $value = implode( ', ', array_filter( array_map( 'strval', $value ) ) );
        } elseif ( ! is_string( $value ) ) {
            $value = '';
        }

        echo '<div class="mb-3">';
        echo   '<label for="' . esc_attr( $id ) . '" class="form-label">' . esc_html( $label ) . '</label>';
        echo   '<input type="text" class="form-control" id="' . esc_attr( $id ) . '" name="custom_mimes" placeholder="jpg, webp, application/pdf" value="' . esc_attr( $value ) . '">';
        echo   '<div class="form-text">' . esc_html( $desc ) . '</div>';
        echo '</div>';
    }

    /**
     * Render Image Constraints block (dimensions, megapixels, aspect, EXIF options).
     */
    protected function imageConstraints()
    {
        echo '<div class="border rounded bg-white mb-3" id="gf-image-constraints-wrap">';
        echo '<div class="fw-semibold mb-2 border-bottom p-3">' . esc_html__( 'Image constraints', 'griffinforms-form-builder' ) . '</div>';

        echo '<div class="p-3">';

        // Row 1: min/max width
        $id_min_w = 'gf-img-min-width';
        $id_max_w = 'gf-img-max-width';
        echo '<div class="row g-3 align-items-end mb-2">';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_min_w) . '" class="form-label">' . esc_html__( 'Min width (px)', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_min_w) . '" name="image_min_width" value="' . esc_attr( $this->image_min_width ) . '">';
        echo '  </div>';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_max_w) . '" class="form-label">' . esc_html__( 'Max width (px)', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_max_w) . '" name="image_max_width" value="' . esc_attr( $this->image_max_width ) . '">';
        echo '  </div>';
        echo '</div>';

        // Row 2: min/max height
        $id_min_h = 'gf-img-min-height';
        $id_max_h = 'gf-img-max-height';
        echo '<div class="row g-3 align-items-end mb-2">';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_min_h) . '" class="form-label">' . esc_html__( 'Min height (px)', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_min_h) . '" name="image_min_height" value="' . esc_attr( $this->image_min_height ) . '">';
        echo '  </div>';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_max_h) . '" class="form-label">' . esc_html__( 'Max height (px)', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_max_h) . '" name="image_max_height" value="' . esc_attr( $this->image_max_height ) . '">';
        echo '  </div>';
        echo '</div>';

        // Row 3: max megapixels
        $id_mp = 'gf-img-max-mp';
        echo '<div class="mb-2">';
        echo '  <label for="' . esc_attr($id_mp) . '" class="form-label">' . esc_html__( 'Max megapixels', 'griffinforms-form-builder' ) . '</label>';
        echo '  <input type="text" inputmode="decimal" pattern="^\\d+(?:\\.\\d+)?$" class="form-control w-25" id="' . esc_attr($id_mp) . '" name="image_max_megapixels" value="' . esc_attr( $this->image_max_megapixels ) . '">';
        echo '  <div class="form-text">' . esc_html__( 'Width × height ÷ 1,000,000. Useful to cap very large images.', 'griffinforms-form-builder' ) . '</div>';
        echo '</div>';

        // Row 4: aspect ratio mode
        $mode = in_array( $this->image_aspect_mode, array('free','preset','custom'), true ) ? $this->image_aspect_mode : 'free';
        $id_mode = 'gf-img-aspect-mode';
        echo '<div class="mb-2">';
        echo '  <label for="' . esc_attr($id_mode) . '" class="form-label">' . esc_html__( 'Aspect ratio', 'griffinforms-form-builder' ) . '</label>';
        echo '  <select id="' . esc_attr($id_mode) . '" name="image_aspect_mode" class="form-select w-auto">';
        echo '    <option value="free"'   . selected($mode, 'free', false)   . '>' . esc_html__( 'Free', 'griffinforms-form-builder' ) . '</option>';
        echo '    <option value="preset"' . selected($mode, 'preset', false) . '>' . esc_html__( 'Preset', 'griffinforms-form-builder' ) . '</option>';
        echo '    <option value="custom"' . selected($mode, 'custom', false) . '>' . esc_html__( 'Custom', 'griffinforms-form-builder' ) . '</option>';
        echo '  </select>';
        echo '</div>';

        // Preset select
        $id_preset = 'gf-img-aspect-preset';
        $preset = $this->image_aspect_preset ?: '4:3';
        echo '<div class="mb-2">';
        echo '  <label for="' . esc_attr($id_preset) . '" class="form-label">' . esc_html__( 'Preset ratio', 'griffinforms-form-builder' ) . '</label>';
        echo '  <select id="' . esc_attr($id_preset) . '" name="image_aspect_preset" class="form-select w-auto">';
        foreach ( array('1:1','3:2','4:3','16:9') as $opt ) {
            echo '<option value="' . esc_attr($opt) . '"' . selected($preset, $opt, false) . '>' . esc_html( $opt ) . '</option>';
        }
        echo '  </select>';
        echo '  <div class="form-text">' . esc_html__( 'Used when Aspect = Preset.', 'griffinforms-form-builder' ) . '</div>';
        echo '</div>';

        // Custom ratio
        $id_cw = 'gf-img-aspect-custom-w';
        $id_ch = 'gf-img-aspect-custom-h';
        echo '<div class="row g-3 align-items-end mb-2">';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_cw) . '" class="form-label">' . esc_html__( 'Custom ratio width', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_cw) . '" name="image_aspect_custom_w" value="' . esc_attr( $this->image_aspect_custom_w ) . '">';
        echo '  </div>';
        echo '  <div class="col-auto">';
        echo '    <label for="' . esc_attr($id_ch) . '" class="form-label">' . esc_html__( 'Custom ratio height', 'griffinforms-form-builder' ) . '</label>';
        echo '    <input type="text" inputmode="numeric" pattern="^\\d+$" class="form-control w-auto" id="' . esc_attr($id_ch) . '" name="image_aspect_custom_h" value="' . esc_attr( $this->image_aspect_custom_h ) . '">';
        echo '  </div>';
        echo '  <div class="col-12 mb-3">';
        echo '    <div class="form-text">' . esc_html__( 'Used when Aspect = Custom. Example 4 and 3 for 4:3.', 'griffinforms-form-builder' ) . '</div>';
        echo '  </div>';
        echo '</div>';

        // Row 5: checkboxes (EXIF)
        $id_rotate = 'gf-img-auto-rotate';
        $id_strip  = 'gf-img-strip-exif';
        echo '<div class="d-flex flex-column gap-2">';
        echo '  <label class="d-inline-flex align-items-center gap-2" for="' . esc_attr($id_rotate) . '">';
        echo '    <input type="checkbox" id="' . esc_attr($id_rotate) . '" name="image_auto_rotate_exif" value="1" ' . checked( (bool)$this->image_auto_rotate_exif, true, false ) . ' />';
        echo '    <span>' . esc_html__( 'Auto‑rotate using EXIF orientation', 'griffinforms-form-builder' ) . '</span>';
        echo '  </label>';
        echo '  <label class="d-inline-flex align-items-center gap-2" for="' . esc_attr($id_strip) . '">';
        echo '    <input type="checkbox" id="' . esc_attr($id_strip) . '" name="image_strip_exif" value="1" ' . checked( (bool)$this->image_strip_exif, true, false ) . ' />';
        echo '    <span>' . esc_html__( 'Strip EXIF metadata on save', 'griffinforms-form-builder' ) . '</span>';
        echo '  </label>';
        echo '</div>';

        echo '<div class="form-text mt-2">' . esc_html__( 'These rules apply to image uploads only and are enforced server‑side. Leave fields blank to avoid enforcing that constraint.', 'griffinforms-form-builder' ) . '</div>';
        echo '</div>';
        echo '</div>';
    }

    /**
     * Render a caution notice specifically for the Code MIME group, clarifying
     * that enabling these types will temporarily expand WordPress' allowed MIME
     * types during upload for this field only.
     */
    protected function renderCodeMimeNotice()
    {
        echo '<div class="alert alert-warning p-2 small mb-3">'
           . esc_html__(
               'Allow code/text files (e.g., JSON, MD, CSV) for this field only during upload. For security, enable only the formats you expect and avoid executable types. WordPress’s allowed types are expanded temporarily just for this field.',
               'griffinforms-form-builder'
             )
           . '</div>';
    }
}