<?php

namespace WeDevs\Wpuf\Pro\Fields;

/**
 * Signature Field Class
 *
 * Signature field for collecting user signatures during registration
 *
 * @since 4.3.0
 */
class Field_Signature extends Profile_Field {

    /**
     * Constructor
     *
     * @since 4.3.0
     */
    public function __construct() {
        $this->name       = __( 'Signature', 'wpuf-pro' );
        $this->input_type = 'signature_field';
        $this->icon       = 'signature';

        // Exclude signature from public user meta fields (REST API and user directory)
        add_filter( 'wpuf_public_user_meta_fields', [ $this, 'exclude_from_public_meta' ], 10, 1 );
    }

    /**
     * Exclude signature from public user meta fields
     *
     * @since 4.3.0
     *
     * @param array $public_fields List of public meta field names
     *
     * @return array Filtered list
     */
    public function exclude_from_public_meta( $public_fields ) {
        // Ensure wpuf_signature is not in the public fields list
        $public_fields = array_diff( $public_fields, [ 'wpuf_signature' ] );
        return array_values( $public_fields ); // Re-index array
    }

    /**
     * Render the Signature field
     *
     * @since 4.3.0
     *
     * @param array  $field_settings
     * @param int    $form_id
     * @param string $type
     * @param int    $post_id
     *
     * @return void
     */
    public function render( $field_settings, $form_id, $type = 'post', $post_id = null ) {
        // Get canvas dimensions based on settings
        $width = isset( $field_settings['canvas_width'] ) ? intval( $field_settings['canvas_width'] ) : 450;
        $height = isset( $field_settings['canvas_height'] ) ? intval( $field_settings['canvas_height'] ) : 150;

        wp_enqueue_script( 'wpuf-signature-pad' );
        wp_enqueue_script( 'wpuf-signature-field-frontend' );
        wp_enqueue_script( 'wpuf-frontend-signature-validation' );
        wp_enqueue_style( 'wpuf-signature-field-frontend' );

        // Apply size presets
        if ( isset( $field_settings['canvas_size'] ) && $field_settings['canvas_size'] !== 'custom' ) {
            switch ( $field_settings['canvas_size'] ) {
                case 'small':
                    $width = 300;
                    $height = 100;
                    break;
                case 'medium':
                    $width = 450;
                    $height = 150;
                    break;
                case 'large':
                    $width = 600;
                    $height = 200;
                    break;
            }
        }

        $show_border = isset( $field_settings['show_border'] ) ? wpuf_validate_boolean( $field_settings['show_border'] ) : true;
        $show_clear = isset( $field_settings['show_clear'] ) ? wpuf_validate_boolean( $field_settings['show_clear'] ) : true;
        $field_id = isset( $field_settings['id'] ) ? $field_settings['id'] : 0;

        $this->field_print_label( $field_settings, $form_id ); ?>

        <div class="wpuf-fields">
            <div class="wpuf-signature-container"
                 data-field-id="<?php echo esc_attr( $field_id ); ?>"
                 data-width="<?php echo esc_attr( $width ); ?>"
                 data-height="<?php echo esc_attr( $height ); ?>"
                 data-required="<?php echo esc_attr( $field_settings['required'] ); ?>">

                <canvas
                    id="signature-canvas-<?php echo esc_attr( $field_id ); ?>"
                    class="signature-canvas <?php echo $show_border ? 'signature-border' : ''; ?>"
                    width="<?php echo esc_attr( $width ); ?>"
                    height="<?php echo esc_attr( $height ); ?>"
                    data-required="<?php echo esc_attr( $field_settings['required'] ); ?>">
                </canvas>

                <input
                    type="hidden"
                    name="<?php echo esc_attr( $field_settings['name'] ); ?>"
                    id="signature-data-<?php echo esc_attr( $field_id ); ?>"
                    class="signature-data"
                    value="">

                <?php if ( $show_clear ): ?>
                    <button type="button" class="button wpuf-clear-signature" data-target="<?php echo esc_attr( $field_id ); ?>">
                        <?php esc_html_e( 'Clear Signature', 'wpuf-pro' ); ?>
                    </button>
                <?php endif; ?>
            </div>
            <?php $this->help_text( $field_settings ); ?>
        </div>

        <?php $this->after_field_print_label();
    }

    /**
     * Get field options setting
     *
     * @since 4.3.0
     *
     * @return array
     */
    public function get_options_settings() {
        // Get default options but exclude the name field since we'll provide our own meta_key field
        $filtered_options = $this->get_default_option_settings( true, [ 'name', 'read_only', 'width' ] );

        $signature_options = [
            [
                'name'      => 'meta_key',
                'title'     => __( 'Meta Key', 'wpuf-pro' ),
                'type'      => 'text',
                'default'   => 'wpuf_signature',
                'section'   => 'basic',
                'priority'  => 11.5,
                'readonly'  => true,
                'help_text' => __( 'The meta key used to store this field data in the database. This is fixed to "wpuf_signature" and cannot be changed.', 'wpuf-pro' ),
            ],

            [
                'name'      => 'canvas_size',
                'title'     => __( 'Canvas Size', 'wpuf-pro' ),
                'type'      => 'select',
                'options'   => [
                    'small'  => __( 'Small (300x100)', 'wpuf-pro' ),
                    'medium' => __( 'Medium (450x150)', 'wpuf-pro' ),
                    'large'  => __( 'Large (600x200)', 'wpuf-pro' ),
                    'custom' => __( 'Custom', 'wpuf-pro' ),
                ],
                'default'   => 'medium',
                'section'   => 'basic',
                'priority'  => 13,
                'help_text' => __( 'Choose a preset size for the signature canvas or select Custom to specify dimensions.', 'wpuf-pro' ),
            ],

            [
                'name'      => 'canvas_width',
                'title'     => __( 'Canvas Width', 'wpuf-pro' ),
                'type'      => 'canvas-width',
                'default'   => 450,
                'section'   => 'basic',
                'priority'  => 13.1,
                'help_text' => __( 'Width of the signature canvas in pixels. Only used when Custom size is selected.', 'wpuf-pro' ),
            ],

            [
                'name'      => 'canvas_height',
                'title'     => __( 'Canvas Height', 'wpuf-pro' ),
                'type'      => 'canvas-height',
                'default'   => 150,
                'section'   => 'basic',
                'priority'  => 13.2,
                'help_text' => __( 'Height of the signature canvas in pixels. Only used when Custom size is selected.', 'wpuf-pro' ),
            ],

            [
                'name'      => 'show_clear',
                'title'     => __( 'Show Clear Button', 'wpuf-pro' ),
                'type'      => 'radio',
                'options'   => [
                    'yes' => __( 'Yes', 'wpuf-pro' ),
                    'no'  => __( 'No', 'wpuf-pro' ),
                ],
                'default'   => 'yes',
                'inline'    => true,
                'section'   => 'advanced',
                'priority'  => 10,
                'help_text' => __( 'Display a button to allow users to clear and redraw their signature.', 'wpuf-pro' ),
            ],

            [
                'name'      => 'show_border',
                'title'     => __( 'Show Border', 'wpuf-pro' ),
                'type'      => 'radio',
                'options'   => [
                    'yes' => __( 'Yes', 'wpuf-pro' ),
                    'no'  => __( 'No', 'wpuf-pro' ),
                ],
                'default'   => 'yes',
                'inline'    => true,
                'section'   => 'advanced',
                'priority'  => 11,
                'help_text' => __( 'Display a border around the signature area.', 'wpuf-pro' ),
            ],
        ];

        return array_merge( $filtered_options, $signature_options );
    }

    /**
     * Get the field props
     *
     * @since 4.3.0
     *
     * @return array
     */
    public function get_field_props() {
        $defaults = $this->default_attributes();
        $props    = [
            'input_type'       => 'signature',
            'label'            => __( 'Signature', 'wpuf-pro' ),
            'meta_key'         => 'wpuf_signature', // Fixed meta key for form builder
            'is_meta'          => 'yes',
            'canvas_size'      => 'medium',
            'canvas_width'     => 450,
            'canvas_height'    => 150,
            'show_clear'       => 'yes',
            'show_border'      => 'yes',
            'id'               => 0,
            'is_new'           => true,
            'show_in_post'     => 'yes',
            'hide_field_label' => 'no',
        ];

        return array_merge( $defaults, $props );
    }

    /**
     * Sanitize SVG content from signature_pad
     *
     * Based on actual signature_pad output, only allows:
     * - <svg> root element with: xmlns, xmlns:xlink, viewBox, width, height
     * - <path> elements with: d, stroke-width, stroke, fill, stroke-linecap
     *
     * @since 4.3.0
     *
     * @param string $svg_string Raw SVG string from signature_pad
     *
     * @return string|WP_Error Sanitized SVG string or WP_Error on failure
     */
    public static function sanitize_svg( $svg_string ) {
        if ( empty( $svg_string ) || ! is_string( $svg_string ) ) {
            return new \WP_Error( 'invalid_input', __( 'Invalid SVG input.', 'wpuf-pro' ) );
        }

        // Check max size (100KB)
        $max_size = 100 * 1024; // 100KB
        if ( strlen( $svg_string ) > $max_size ) {
            return new \WP_Error( 'svg_too_large', __( 'SVG file is too large. Maximum size is 100KB.', 'wpuf-pro' ) );
        }

        // Must start with <svg
        if ( ! preg_match( '/^<svg/i', trim( $svg_string ) ) ) {
            return new \WP_Error( 'invalid_svg', __( 'Invalid SVG format. Must start with <svg element.', 'wpuf-pro' ) );
        }

        // Disable external entity loading for security
        $old_value = libxml_disable_entity_loader( true );
        libxml_use_internal_errors( true );

        // Parse SVG as XML
        $dom = new \DOMDocument( '1.0', 'UTF-8' );
        $dom->formatOutput = false;
        $dom->preserveWhiteSpace = true;

        // Load XML - handle escaped quotes from POST data
        $svg_string = stripslashes( $svg_string );
        $loaded = $dom->loadXML( $svg_string );

        // Restore libxml settings
        libxml_disable_entity_loader( $old_value );
        libxml_clear_errors();

        if ( ! $loaded ) {
            return new \WP_Error( 'xml_parse_error', __( 'Invalid XML structure in SVG.', 'wpuf-pro' ) );
        }

        // Get root SVG element
        $svg = $dom->getElementsByTagName( 'svg' )->item( 0 );
        if ( ! $svg ) {
            return new \WP_Error( 'no_svg_element', __( 'No SVG element found.', 'wpuf-pro' ) );
        }

        // Whitelist of allowed attributes for <svg> element
        $allowed_svg_attrs = [ 'xmlns', 'xmlns:xlink', 'viewBox', 'width', 'height' ];

        // Remove disallowed attributes from <svg>
        $svg_attrs_to_remove = [];
        foreach ( $svg->attributes as $attr ) {
            if ( ! in_array( $attr->nodeName, $allowed_svg_attrs, true ) ) {
                $svg_attrs_to_remove[] = $attr->nodeName;
            }
        }
        foreach ( $svg_attrs_to_remove as $attr_name ) {
            $svg->removeAttribute( $attr_name );
        }

        // Validate and sanitize <path> elements
        $paths = $dom->getElementsByTagName( 'path' );
        $path_count = $paths->length;

        // Check max path count (200)
        if ( $path_count > 200 ) {
            return new \WP_Error( 'too_many_paths', __( 'SVG contains too many path elements. Maximum is 200.', 'wpuf-pro' ) );
        }

        // Whitelist of allowed attributes for <path> elements
        $allowed_path_attrs = [ 'd', 'stroke-width', 'stroke', 'fill', 'stroke-linecap' ];

        // Process each path element
        $paths_to_remove = [];
        for ( $i = 0; $i < $path_count; $i++ ) {
            $path = $paths->item( $i );

            // Remove disallowed attributes
            $path_attrs_to_remove = [];
            foreach ( $path->attributes as $attr ) {
                $attr_name = $attr->nodeName;

                // Check if attribute is allowed
                if ( ! in_array( $attr_name, $allowed_path_attrs, true ) ) {
                    $path_attrs_to_remove[] = $attr_name;
                    continue;
                }

                // Validate specific attribute values
                if ( 'fill' === $attr_name && 'none' !== $attr->nodeValue ) {
                    // signature_pad always uses fill="none"
                    $path->setAttribute( 'fill', 'none' );
                } elseif ( 'stroke-linecap' === $attr_name && 'round' !== $attr->nodeValue ) {
                    // signature_pad always uses stroke-linecap="round"
                    $path->setAttribute( 'stroke-linecap', 'round' );
                } elseif ( 'stroke' === $attr_name ) {
                    // Validate stroke is rgb/rgba format
                    $stroke_value = $attr->nodeValue;
                    if ( ! preg_match( '/^rgb\(|^rgba\(/i', $stroke_value ) ) {
                        $path_attrs_to_remove[] = $attr_name;
                    }
                } elseif ( 'stroke-width' === $attr_name ) {
                    // Validate stroke-width is numeric
                    if ( ! is_numeric( $attr->nodeValue ) ) {
                        $path_attrs_to_remove[] = $attr_name;
                    }
                }
            }

            // Remove disallowed attributes
            foreach ( $path_attrs_to_remove as $attr_name ) {
                $path->removeAttribute( $attr_name );
            }

            // Path must have 'd' attribute
            if ( ! $path->hasAttribute( 'd' ) ) {
                $paths_to_remove[] = $path;
            }
        }

        // Remove invalid paths
        foreach ( $paths_to_remove as $path ) {
            $path->parentNode->removeChild( $path );
        }

        // Remove any elements that are not <path>
        $all_elements = $dom->getElementsByTagName( '*' );
        $elements_to_remove = [];
        foreach ( $all_elements as $element ) {
            if ( 'svg' !== $element->nodeName && 'path' !== $element->nodeName ) {
                $elements_to_remove[] = $element;
            }
        }
        foreach ( $elements_to_remove as $element ) {
            $element->parentNode->removeChild( $element );
        }

        // Get sanitized SVG string
        $sanitized = $dom->saveXML( $svg );

        return $sanitized;
    }

    /**
     * Upload and save signature SVG file
     *
     * @since 4.3.0
     *
     * @param string $svg_string Sanitized SVG string
     * @param int    $user_id    User ID
     * @param int    $form_id    Form ID (optional)
     *
     * @return string|WP_Error Relative file path or WP_Error on failure
     */
    public static function upload_signature( $svg_string, $user_id, $form_id = 0 ) {
        // Sanitize SVG first
        $sanitized_svg = self::sanitize_svg( $svg_string );
        if ( is_wp_error( $sanitized_svg ) ) {
            return $sanitized_svg;
        }

        // Get upload directory
        $upload_dir = wp_upload_dir();
        $base_dir = $upload_dir['basedir'];

        // Create signature directory structure: wpuf/signatures/YYYY/MM/
        $year = date( 'Y' );
        $month = date( 'm' );
        $signature_dir = $base_dir . '/wpuf/signatures/' . $year . '/' . $month . '/';

        // Check if base wpuf directory exists
        $wpuf_base_dir = $base_dir . '/wpuf';
        if ( ! file_exists( $wpuf_base_dir ) ) {
            wp_mkdir_p( $wpuf_base_dir );
            // Create index.php in wpuf directory
            $wpuf_index = $wpuf_base_dir . '/index.php';
            if ( ! file_exists( $wpuf_index ) ) {
                file_put_contents( $wpuf_index, '<?php // Silence is golden' );
            }
        }

        // Create directories if they don't exist
        if ( ! wp_mkdir_p( $signature_dir ) ) {
            return new \WP_Error( 'directory_creation_failed', __( 'Failed to create signature directory.', 'wpuf-pro' ) );
        }

        // Create index.php for security if it doesn't exist
        $index_file = $signature_dir . 'index.php';
        if ( ! file_exists( $index_file ) ) {
            file_put_contents( $index_file, '<?php // Silence is golden' );
        }

        // Generate unique filename: {user_id}_{form_id}_{timestamp}.svg
        $timestamp = time();
        $filename = $user_id . '_' . $form_id . '_' . $timestamp . '.svg';
        $file_path = $signature_dir . $filename;

        // Write SVG file
        $written = file_put_contents( $file_path, $sanitized_svg );
        if ( false === $written ) {
            return new \WP_Error( 'file_write_failed', __( 'Failed to write signature file.', 'wpuf-pro' ) );
        }

        // Set file permissions (644)
        chmod( $file_path, 0644 );

        // Return relative path from uploads directory
        $relative_path = 'wpuf/signatures/' . $year . '/' . $month . '/' . $filename;

        return $relative_path;
    }

    /**
     * Get signature URL for a user
     *
     * @since 4.3.0
     *
     * @param int $user_id User ID
     *
     * @return string|false Signature URL or false if not found
     */
    public static function get_signature_url( $user_id ) {
        $signature_path = get_user_meta( $user_id, 'wpuf_signature', true );

        if ( empty( $signature_path ) ) {
            return false;
        }

        $upload_dir = wp_upload_dir();
        $file_path = $upload_dir['basedir'] . '/' . $signature_path;

        // Verify file exists
        if ( ! file_exists( $file_path ) ) {
            return false;
        }

        return $upload_dir['baseurl'] . '/' . $signature_path;
    }

    /**
     * Delete signature file and user meta
     *
     * @since 4.3.0
     *
     * @param int $user_id User ID
     *
     * @return bool True on success, false on failure
     */
    public static function delete_signature( $user_id ) {
        $signature_path = get_user_meta( $user_id, 'wpuf_signature', true );

        if ( empty( $signature_path ) ) {
            return true; // No signature to delete
        }

        $upload_dir = wp_upload_dir();
        $file_path = $upload_dir['basedir'] . '/' . $signature_path;

        // Delete file if it exists
        if ( file_exists( $file_path ) ) {
            wp_delete_file( $file_path );
        }

        // Delete user meta
        delete_user_meta( $user_id, 'wpuf_signature' );

        return true;
    }

    /**
     * Sanitize field data - preserve SVG string
     *
     * This method is called by prepare_meta_fields() to sanitize the field data
     * without stripping SVG tags (which sanitize_text_field would do)
     *
     * @since 4.3.0
     *
     * @param mixed $data  Raw field data
     * @param array $field Field settings
     *
     * @return string Sanitized SVG string or empty string
     */
    public function sanitize_field_data( $data, $field ) {
        if ( empty( $data ) || ! is_string( $data ) ) {
            return '';
        }

        // Check if it's SVG format
        if ( ! preg_match( '/^<svg/i', trim( $data ) ) ) {
            return '';
        }

        // Return the SVG string as-is (it will be sanitized during upload)
        // We use wp_kses_post to allow SVG tags but this will still strip some attributes
        // So we'll do minimal sanitization here and full sanitization in upload_signature()
        return $data;
    }

    /**
     * Prepare entry - process signature data
     *
     * @since 4.3.0
     *
     * @param array $field Field settings
     *
     * @return mixed Processed signature data or empty string
     */
    public function prepare_entry( $field ) {
        check_ajax_referer( 'wpuf_form_add' );

        // Get raw POST data for signature field
        $field_name = isset( $field['name'] ) ? $field['name'] : '';
        $raw_value = isset( $_POST[ $field_name ] ) ? wp_unslash( $_POST[ $field_name ] ) : '';

        if ( empty( $raw_value ) || ! is_string( $raw_value ) ) {
            return '';
        }

        // Check if it's SVG format
        if ( ! preg_match( '/^<svg/i', trim( $raw_value ) ) ) {
            return '';
        }

        // Return raw SVG string - will be processed during form submission
        // The actual file upload will happen in the form submission hook
        return $raw_value;
    }

    /**
     * Render field data for display
     *
     * @since 4.3.0
     *
     * @param mixed $data
     * @param array $field
     *
     * @return string
     */
    public function render_field_data( $data, $field ) {
        $data       = is_array( $data ) ? array_pop( $data ) : $data;
        $hide_label = isset( $field['hide_field_label'] )
            ? wpuf_validate_boolean( $field['hide_field_label'] )
            : false;

        $container_classnames = [ 'wpuf-field-data', 'wpuf-field-data-' . $this->input_type ];

        if ( empty( $data ) ) {
            return '';
        }

        // Get signature URL - data could be a file path or URL
        $signature_url = false;

        if ( is_string( $data ) ) {
            // Check if it's a relative file path (stored in user meta)
            if ( preg_match( '/^wpuf\/signatures\//', $data ) ) {
                $upload_dir = wp_upload_dir();
                $file_path = $upload_dir['basedir'] . '/' . $data;
                if ( file_exists( $file_path ) ) {
                    $signature_url = $upload_dir['baseurl'] . '/' . $data;
                }
            } elseif ( filter_var( $data, FILTER_VALIDATE_URL ) ) {
                // It's already a full URL
                $signature_url = $data;
            } elseif ( preg_match( '/^<svg/i', $data ) ) {
                // It's an SVG string - convert to data URI for display
                $signature_url = 'data:image/svg+xml;base64,' . base64_encode( $data );
            }
        }

        ob_start();
        ?>
            <li class="<?php echo esc_attr( implode( ' ' , $container_classnames ) );  ?>">
                <?php if ( ! $hide_label ): ?>
                    <label><?php echo esc_html( $field['label'] ); ?>:</label>
                <?php endif; ?>
                <?php if ( $signature_url ): ?>
                    <img src="<?php echo esc_url( $signature_url ); ?>" alt="<?php echo esc_attr__( 'Signature', 'wpuf-pro' ); ?>" style="max-width: 300px; height: auto; border: 1px solid #ddd;" />
                <?php else: ?>
                    <?php esc_html_e( 'Signature provided', 'wpuf-pro' ); ?>
                <?php endif; ?>
            </li>
        <?php
        return ob_get_clean();
    }
}
