<?php

namespace ASENHA\Classes;

/**
 * Class for Email Address Obfuscator module
 *
 * @since 6.9.5
 */
class Email_Address_Obfuscator {

    /**
     * Cached associative array of allowed upload extensions (lowercase), e.g. [ 'jpg' => true ].
     *
     * Derived from WordPress core `get_allowed_mime_types()`, which respects `upload_mimes` filters.
     *
     * @since 7.??.? (ASE)
     * @var array|null
     */
    private static $allowed_upload_extensions = null;

    /**
     * Get allowed upload extensions for the Media Library.
     *
     * @since 7.??.? (ASE)
     *
     * @return array Associative array keyed by extension (lowercase) for fast lookup.
     */
    private function get_allowed_upload_extensions__premium_only() {
        if ( null !== self::$allowed_upload_extensions ) {
            return self::$allowed_upload_extensions;
        }

        $allowed_mime_types = get_allowed_mime_types();
        $extensions         = array();

        foreach ( array_keys( $allowed_mime_types ) as $ext_group ) {
            if ( empty( $ext_group ) || ! is_string( $ext_group ) ) {
                continue;
            }

            $parts = explode( '|', strtolower( $ext_group ) );

            foreach ( $parts as $ext ) {
                $ext = trim( $ext );
                if ( '' === $ext ) {
                    continue;
                }
                $extensions[ $ext ] = true;
            }
        }

        self::$allowed_upload_extensions = $extensions;

        return self::$allowed_upload_extensions;
    }

    /**
     * Get the render mode used by obfuscate shortcode output.
     *
     * @since 8.4.3
     *
     * @return string Render mode.
     */
    private function get_obfuscate_email_render_mode() {
        $render_mode = 'legacy';

        $options = get_option( ASENHA_SLUG_U, array() );
        $builder_safe_mode = isset( $options['obfuscate_email_address_builder_safe_mode'] ) ? $options['obfuscate_email_address_builder_safe_mode'] : false;

        if ( 'on' === $builder_safe_mode
            || '1' === $builder_safe_mode
            || 1 === $builder_safe_mode
            || true === $builder_safe_mode
        ) {
            // builder-safe / high-compatibility mode
            $render_mode = 'builder_safe';
        }

        return $render_mode;
    }

    /**
     * Obfuscate email address on the frontend using antispambot() native WP function
     * 
     * @link: https://gist.github.com/eclarrrk/349360b52e8822b69cb6fc499722520f
     * @since 5.5.0
     */
    public function obfuscate_string( $atts ) {
        
        $atts = shortcode_atts( array(
            'email'     => '',
            'subject'   => '',
            'text'      => '',
            'display'   => 'newline',
            'link'      => 'no',
            'class'     => '',
        ), $atts );

        $email = sanitize_email( $atts['email'] );

        if ( ! is_email( $email ) ) {
            return '';
        }

        $render_mode = $this->get_obfuscate_email_render_mode();
        $css_bidi_styles = '';
        $direction_styles = '';

        if ( ! empty( $atts['text'] ) ) {
            $text = esc_html( $atts['text'] );
        } else {
            if ( 'legacy' === $render_mode ) {
                // Reverse email address characters if not in Firefox, which has bug related to unicode-bidi CSS property.
                $http_user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) : 'generic';
                if ( false !== stripos( $http_user_agent, 'firefox' )
                    || false !== stripos( $http_user_agent, 'iphone' )
                ) {
                    // Do nothing. Do not reverse characters.
                    $email_reversed = $email;
                    $email_rev_parts = explode( '@', $email_reversed );
                    $email_rev_parts = array( $email_rev_parts[0], $email_rev_parts[1] );
                    $css_bidi_styles = '';
                    $direction_styles = 'direction:rtl;';
                } else {
                    $email_reversed = strrev( $email );
                    $email_rev_parts = explode( '@', $email_reversed );
                    $css_bidi_styles = 'unicode-bidi:bidi-override;';
                    $direction_styles = 'direction:rtl;';
                }

                $random_number = dechex( rand( 1000000, 9999999 ) );
                $text = esc_html( $email_rev_parts[0] ) . '<span style="display:none;">obfsctd-' . esc_html( $random_number ) . '</span>&#64;' . esc_html( $email_rev_parts[1] );
            } else {
                // builder-safe / high-compatibility mode
                $text = antispambot( $email );
            }
        }

        $display = sanitize_text_field( $atts['display'] );
        if ( ! in_array( $display, array( 'newline', 'inline' ), true ) ) {
            $display = 'newline';
        }

        if ( 'newline' === $display ) {
            if ( 'legacy' === $render_mode ) {
                $display_css = 'display:flex;justify-content:flex-end;';
            } else {
                $display_css = 'display:block;';
            }
        } else {
            $display_css = 'display:inline;';
        }

        $subject = sanitize_text_field( $atts['subject'] );
        if ( ! empty( $subject ) ) {
            $subject = '?subject=' . rawurlencode( $subject );
        }
        $link = sanitize_text_field( $atts['link'] );
        if ( ! in_array( $link, array( 'no', 'yes', 'mailto' ), true ) ) {
            $link = 'no';
        }

        $class = sanitize_text_field( $atts['class'] );
        $span_style = $display_css . $css_bidi_styles . $direction_styles;
        
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            
            if ( 'yes' === $link ) {
                return '<a href="' . esc_attr( 'mailto:' . antispambot( $email, 1 ) . $subject ) . '" class="' . esc_attr( $class ) . '">' . '<span style="' . esc_attr( $span_style ) . '">' . $text . '</span>' . '</a>';
            } elseif ( 'mailto' === $link ) {
                return 'mailto:' . esc_html( antispambot( $email, 1 ) );
            } else {    
                return '<span style="' . esc_attr( $span_style ) . '" class="' . esc_attr( $class ) . '">' . $text . '</span>';
            }

        } else {

            return '<span style="' . esc_attr( $span_style ) . '" class="' . esc_attr( $class ) . '">' . $text . '</span>';
            
        }   
        
    }
    
    /**
     * Add additional attributes to the list of safe CSS attributes
     * This prevents those attributes from being stripped out when displaying the obfuscated email address
     * 
     * @since 7.3.1
     */
    public function add_additional_attributes_to_safe_css( $css_attributes ) {
        $css_attributes[] = 'display';
        $css_attributes[] = 'unicode-bidi';

        return $css_attributes;
    }
    
    /**
     * Replace email addresses in post content with the obfuscation shortcode
     * 
     * @since 6.2.1
     */
    public function obfuscate_emails_in_content__premium_only( $content ) {
        $options = get_option( ASENHA_SLUG_U, array() );
        $obfuscate_for_visitor_only = isset( $options['obfuscate_email_address_visitor_only'] ) ? $options['obfuscate_email_address_visitor_only'] : false;

        // Regex pattern for an email address
        // $pattern = '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/';
        // Regex pattern for an email address preceeded by double qoute, space or >
        $pattern = '/(?:^|[>"\s])([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/';

        if ( $obfuscate_for_visitor_only ) {
            if ( is_user_logged_in() ) {
                // Do nothing. Do not obfuscate email addresses.
            } else {
                $content = preg_replace_callback( $pattern, [ $this, 'replace_email_with_shortcode__premium_only' ], $content );
            }        
        } else {
            $content = preg_replace_callback( $pattern, [ $this, 'replace_email_with_shortcode__premium_only' ], $content );
        }
        
        return $content;        
    }
    
    /**
     * Replace each email with the obfuscation shortcode
     * 
     * @since 6.2.1
     */
    public function replace_email_with_shortcode__premium_only( $matches ) {
        $email = $matches[0];

        /*
         * Avoid obfuscating filename-like strings that contain '@' and end with an allowed upload
         * extension, e.g. logo@2x.png or some-@-document.pdf.
         */
        if ( isset( $email[0] ) && '"' !== $email[0] ) {
            $email_candidate = $email;

            if ( ' ' === $email_candidate[0] || '>' === $email_candidate[0] ) {
                $email_candidate = substr( $email_candidate, 1 );
            }

            $extension = strtolower( pathinfo( $email_candidate, PATHINFO_EXTENSION ) );

            if ( '' !== $extension ) {
                $allowed_upload_extensions = $this->get_allowed_upload_extensions__premium_only();
                if ( isset( $allowed_upload_extensions[ $extension ] ) ) {
                    return $email;
                }
            }
        }

        if ( ' ' == $email[0] ) {
            // Matched email is preceeded by a blank space, let's replace it with the obfuscate shortcode
            // so the email addres is obfuscated
            $email = substr( $email, 1 );
            $shortcode = '[obfuscate email="' . $email . '" display="inline"]';
            return " " . do_shortcode( $shortcode );            
        } elseif ('>' == $email[0]) {
            // Matched email is preceeded by a ">", e.g. inside a <div> or <span> 
            // let's replace it with the obfuscate shortcode, so the email addres is obfuscated
            // $email = substr($email, 1);
            $email = substr( $email, 1 );
            $shortcode = '[obfuscate email="' . $email . '" display="inline"]';
            return ">" . do_shortcode( $shortcode );
        } elseif ( '"' == $email[0] ) {
            // Matched email is preceeded by double quote, it's already part of the obfuscate shortcode.
            // Let's return as is, so the shortcode can be executed as is.
            return $email;
        } else {}
    }

}