<?php
    /**
     * Base class for all widgets
     * 
     * @see WP_Widget
     * @package Blog Bee
     * @since 1.0.0
     */
    namespace BBE\Widgets;
    use WP_Widget;

    if( ! class_exists( __NAMESPACE__ . '\\Base' ) ) {
        /**
         * Base class
         */
        abstract class Base extends WP_Widget {
            /**
             * An array of all the widget
             * 
             * @since 1.0.0
             */
            private static $widget_classes = [];

            /**
             * MARK: update
             * Updates a particular instance of a widget.
             *
             * This function should check that `$new_instance` is set correctly. The newly-calculated
             * value of `$instance` should be returned. If false is returned, the instance won't be
             * saved/updated.
             *
             * @param array $new_instance New settings for this instance as input by the user via
             *                            WP_Widget::form().
             * @param array $old_instance Old settings for this instance.
             * @return array Settings to save or bool false to cancel saving.
             */
            public function update( $new_instance, $old_instance ){
                $instance = $old_instance;
                $widget_fields = static::widget_fields();
                if( ! is_array( $widget_fields ) ) return;
                foreach( $widget_fields as $widget_field ) {
                    $instance[ $widget_field[ 'name' ] ] = $this->sanitize_widget_values( $widget_field, $new_instance );
                }
                return $instance;
            }

            /**
             * MARK: Sanitization
             * Sanitizes the values for a widget field based on its type.
             *
             * @param array $widget_field The widget field definition.
             * @param array $new_instance The new instance values.
             * @return mixed The sanitized value.
             */
            private function sanitize_widget_values( $widget_field, $new_instance ) {
                $name = $widget_field[ 'name' ];
                $type = $widget_field[ 'type' ];

                if( in_array( $type, [ 'text', 'select', 'multicheckbox' ] ) ) {

                    return sanitize_text_field( $new_instance[ $name ] );

                } elseif ( $type === 'checkbox' ) {

                    return ! empty( $new_instance[ $name ] ) ? true : false;

                } elseif ( in_array( $type , [ 'number', 'upload' ]) ) {

                    return absint( $new_instance[ $name ] );

                } elseif ( $type === 'textarea' ) {

                    return wp_kses_post( $new_instance[ $name ] );

                } elseif ( $type === 'url' ) {

                    return esc_url_raw( $new_instance[ $name ] );

                } elseif ( $type === 'radio' ) {

                    $fields = array_keys( $widget_field[ 'choices' ] );

                    if( in_array( $new_instance[ $name ], $fields ) ) {
                        return $new_instance[ $name ];
                    }

                } elseif ( $type === 'multiselect' ) {

                    $new_value = $new_instance[ $name ] ?? [];

                    return array_map( 'absint', ( array ) $new_value );

                } else {
                    
                    return isset( $new_instance[ $name ] ) ? sanitize_text_field( $new_instance[ $name ] ) : '';

                }
            }

            /**
             * Register widgets
             * 
             * @since 1.0.0
             */
            public static function register_widgets() {
                add_action( 'widgets_init', [ self::class, 'init_widgets' ] );
            }

            /**
             * Initialize widgets
             * 
             * @since 1.0.0
             */
            public static function init_widgets() {
                $widgets = self::get_widgets();
                if( ! empty( $widgets ) && is_array( $widgets ) ) {
                    foreach( $widgets as $widget ) {
                        register_widget( $widget );
                    }
                }
            }

            /**
             * Get widgets
             * 
             * @since 1.0.0
             */
            public static function get_widgets() {
                return self::$widget_classes;
            }

            /**
             * Set widget
             * 
             * @param string $widget_class The widget class to register
             * @since 1.0.0
             */
            public static function set_widget( $widget_class ) {
                self::$widget_classes[] = $widget_class;
            }

            /**
             * A method that every widget class must implement to define its fields
             * 
             * @since 1.0.0
             */
            abstract public static function widget_fields();

            /**
             * MARK: FORM
             * Render fields in widget
             * 
             * @param array $instance The settings for the particular instance of the widget
             * @since 1.0.0
             * @override
             */
            public function form( $instance ) {
                $fields = static::widget_fields();
                echo '<div class="bbe-widget-fields ' . esc_attr( $this->id_base ) . '">';
                    if( ! empty( $fields ) && is_array( $fields ) ) {
                        foreach( $fields as $field ) {
                            if( isset( $instance[ $field[ 'name' ] ] ) ) {
                                $value = $instance[ $field[ 'name' ] ];
                            } elseif ( isset( $field[ 'default' ] ) ) {
                                $value = $field[ 'default' ];
                            } else {
                                $value = '';
                            }
                            $this->render_widget_field( $field, $value );
                        }
                    }
                    static::custom_widget_fields();
                echo '</div>';
            }

            /**
             * Add custom widget fields
             * 
             * @since 1.0.0
             */
            protected function custom_widget_fields(){}

            /**
             * Render a widget field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_widget_field( $field, $value ) {
                $type = $field[ 'type' ];
                $name = $field[ 'name' ];
                $class = 'field ' . $type . ' ' . $name;

                echo '<div class="' . esc_attr( $class ) . '">'; 

                    switch( $type ) :

                        case 'select':
                                $this->render_select_field( $field, $value );
                            break;

                        case 'checkbox':
                                $this->render_checkbox_field( $field, $value );
                            break;

                        case 'textarea':
                                $this->render_textarea_field( $field, $value );
                            break;

                        case 'radio':
                                $this->render_radio_field( $field, $value );
                            break;

                        case 'upload':
                                $this->render_upload_field( $field, $value );
                            break;

                        case 'heading':
                                $this->render_heading_field( $field );
                            break;

                        case 'url':
                                $this->render_url_field( $field, $value );
                            break;

                        case 'number':
                                $this->render_number_field( $field, $value );
                            break;

                        case 'multiselect':
                                $this->render_multiselect_field( $field, $value );
                            break;

                        default:
                                $this->render_text_field( $field, $value );
                            break;

                    endswitch;

                echo '</div>';
            }

            /**
             * Get field header
             * 
             * @param array $field The widget field definition
             * @since 1.0.0
             */
            private function get_field_header( $field, $render_description = true ) {

                if( isset( $field[ 'title' ] ) ) echo '<h2 class="title">' . esc_html( $field[ 'title' ] ) . '</h2>';

                if( $render_description && isset( $field[ 'description' ] ) ) echo '<p class="description">' . esc_html( $field[ 'description' ] ) . '</p>';

            }

            /**
             * Render heading field
             * 
             * @param array $field The widget field definition
             * @since 1.0.0
             */
            private function render_heading_field( $field ) {

                if( isset( $field[ 'title' ] ) ) echo '<h2 class="title">' . esc_html( $field[ 'title' ] ) . '</h2>';
            }

            /**
             * Render radio field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_radio_field( $field, $value ) {

                $this->get_field_header( $field );

                if( ! empty( $field[ 'choices' ] ) && is_array( $field[ 'choices' ] ) ) :

                    foreach( $field[ 'choices' ] as $choice => $label ) :

                        echo '<p>';

                            echo '<input 
                                class = "widefat"
                                id = "' . esc_attr( $this->get_field_id( $field[ 'name' ] ) . '-' . $choice ) . '"
                                name = "' . esc_attr( $this->get_field_name( $field[ 'name' ] ) ) . '"
                                type = "radio"
                                value = "' . esc_attr( $choice ) .'"
                                ' . checked( $value, $choice, false ) . '
                            />';

                            echo '<label for="' . esc_attr( $this->get_field_id( $field[ 'name' ] ) . '-' . $choice ) . '">' . esc_html( $label ) . '</label>';

                        echo '</p>';

                    endforeach;

                endif;
            }

            /**
             * Render text field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_text_field( $field, $value ) {

                $this->get_field_header( $field );

                ?>
                    <input 
                        class = "widefat"
                        id = "<?php echo esc_attr( $this->get_field_id( $field[ 'name' ] ) ); ?>"
                        name = "<?php echo esc_attr( $this->get_field_name( $field[ 'name' ] ) ); ?>"
                        type = "text"
                        value = "<?php echo esc_attr( $value ); ?>"
                    />

                <?php
            }

            /**
             * Render url field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_url_field( $field, $value ) {

                $this->get_field_header( $field );

                ?>
                    <input 
                        class = "widefat"
                        id = "<?php echo esc_attr( $this->get_field_id( $field[ 'name' ] ) ); ?>"
                        name = "<?php echo esc_attr( $this->get_field_name( $field[ 'name' ] ) ); ?>"
                        type = "url"
                        placeholder = <?php echo esc_attr__( 'www.example.com', 'blog-bee' ); ?>
                        value = "<?php echo esc_attr( $value ); ?>"
                    />

                <?php
            }

            /**
             * Render textarea field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_textarea_field( $field, $value ) {

                $this->get_field_header( $field );

                ?>
                    <textarea
                        class = "widefat"
                        id = "<?php echo esc_attr( $this->get_field_id( $field[ 'name' ] ) ); ?>"
                        name = "<?php echo esc_attr( $this->get_field_name( $field[ 'name' ] ) ); ?>"
                    >

                        <?php echo wp_kses_post( $value ); ?>

                    </textarea>

                <?php
            }

            /**
             * Render text field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_multiselect_field( $field, $value ) {

                $this->get_field_header( $field );

                echo '<select class="widefat" id="' . esc_attr( $this->get_field_id( $field[ 'name' ] ) ) . '" name="' . esc_attr( $this->get_field_name( $field[ 'name' ] ) . '[]' ) . '" data-fetch="' . esc_attr( $field[ 'fetch' ] ) . '" multiple>';

                    foreach( $value as $id ) :

                        if( $field[ 'fetch' ] === 'post' ) {
                            $label = get_the_title( $id );
                        } else {
                            $label = get_cat_name( $id );
                        }
                        echo '<option value="' . esc_attr( $id ) . '" selected>' . esc_html( $label ) . '</option>';

                    endforeach;

                echo '</select>';

            }

            /**
             * Render checkbox field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_checkbox_field( $field, $value ) {
                echo '<input 
                    class = "widefat"
                    id = "' . esc_attr( $this->get_field_id( $field[ 'name' ] ) ) . '"
                    name = "' . esc_attr( $this->get_field_name( $field[ 'name' ] ) ) . '"
                    type = "checkbox"
                    value = "' . esc_attr( $value ) . '"
                    ' . checked( $value, true, false ) . '
                />';
                
                echo '<label for="' . esc_attr( $this->get_field_id( $field[ 'name' ] ) ) . '">' . esc_html( $field[ 'title' ] ) . '</label>';

            }

            /**
             * Render number field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_number_field( $field, $value ) {

                $this->get_field_header( $field );

                ?>
                    <input 
                        class = "widefat"
                        id = "<?php echo esc_attr( $this->get_field_id( $field[ 'name' ] ) ); ?>"
                        name = "<?php echo esc_attr( $this->get_field_name( $field[ 'name' ] ) ); ?>"
                        type = "number"
                        value = "<?php echo esc_attr( $value ); ?>"
                        min = "<?php echo esc_attr( $field[ 'input_attrs' ][ 'min' ] ); ?>"
                        max = "<?php echo esc_attr( $field[ 'input_attrs' ][ 'max' ] ); ?>"
                        step = "<?php echo esc_attr( $field[ 'input_attrs' ][ 'step' ] ); ?>"
                    />

                <?php
            }

            /**
             * Render select field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_select_field( $field, $value ) {

                $this->get_field_header( $field );

                if( isset( $field[ 'choices' ] ) && is_array( $field[ 'choices' ] ) ) :

                    echo '<select class="widefat" id="' . esc_attr( $this->get_field_id( $field[ 'name' ] ) ) . '" name="' . esc_attr( $this->get_field_name( $field[ 'name' ] ) ) . '">';

                        foreach( $field[ 'choices' ] as $choice_key => $choice_value ) :

                            echo '<option value="' . esc_attr( $choice_key ) . '" ' . selected( $choice_key, $value, false ) . '>' . esc_html( $choice_value ) . '</option>';

                        endforeach;

                    echo '</select>';

                endif;
            }

            /**
             * Render upload field
             * 
             * @param array $field The widget field definition
             * @param mixed $value The value of the widget field
             * @since 1.0.0
             */
            private function render_upload_field( $field, $value ) {

                $this->get_field_header( $field );

                $class = 'upload-figure';
                $src = '';
                if( $value ) {
                    $class .= ' has-image';
                    $src_args = wp_get_attachment_image_src( $value, 'large' );
                    if( isset( $src_args[ 0 ] ) ) $src = $src_args[ 0 ];
                }

                ?>

                    <figure class="<?php echo esc_attr( $class ); ?>">
                        <img src="<?php echo esc_url( $src ); ?>" class="upload-image" />
                        <button class="upload-button" type="button"><?php esc_html_e( 'Add Image', 'blog-bee' ); ?></button>
                    </figure>

                    <input 
                        class = "widefat"
                        id = "<?php echo esc_attr( $this->get_field_id( $field[ 'name' ] ) ); ?>"
                        name = "<?php echo esc_attr( $this->get_field_name( $field[ 'name' ] ) ); ?>"
                        type = "hidden"
                        value = "<?php echo esc_attr( $value ); ?>"
                    />

                <?php
            }

            /**
             * MARK: Load More Button
             * Render ajax load more button
             * 
             * @since 1.0.0
             */
            protected function get_load_more_button() {
                ?>
                    <button class="load-more" type="button">
                        <?php esc_html_e( 'Load More', 'blog-bee' ); ?>
                    </button>
                <?php
            }
        }
    }