<?php
    /**
     * Schema Manager
     * 
     * @package Blog Bee
     * @since 1.0.0
     */
    namespace BBE;

    use BBE\Query_Manager as Query_Manager;
    use BBE\Customizer\Theme_Defaults as Theme_Defaults;

    use function add_action;
    use function home_url;
    use function get_bloginfo;
    use function get_theme_mod;
    use function wp_get_attachment_image_src;
    use function wp_json_encode;

    if( ! class_exists( __NAMESPACE__ . '\\Schema_Manager' ) ) {
        /**
         * Schema_Manager Class
         */
        class Schema_Manager {
            /**
             * Schema graph
             * 
             * @since 1.0.0
             * @var array
             */
            private $graph = [];

            /**
             * Constructor
             * 
             * @since 1.0.0
             */
            public function __construct() {
                add_action( 'init', [ $this, 'init' ] );
            }

            /**
             * Initialization
             * 
             * Ensure the schema code only executes when the
             * enable schema(in seo/misc section) option in
             * the customizer is turned on
             * 
             * @since 1.0.0
             */
            public function init() {
                if( Theme_Defaults::get_theme_option( 'seo_misc_enable_schema' ) ) {
                    add_action( 'wp_footer', [ $this, 'get_schema_script' ], 999 );
                }
            }

            /**
             * Get scheme script tag
             * 
             * @since 1.0.0
             */
            public function get_schema_script() {
                /**
                 * Populate the graph private variable.
                 */
                $this->schema_site();
                $this->schema_post();
                $this->schema_archive();
                $this->schema_page();
                $this->schema_search();
                $this->schema_404();

                /**
                 * A guard to ensure no empty script tag is rendered.
                 */
                if( empty( $this->graph ) ) return;

                $encode_args = [
                    '@context' => 'https://schema.org',
                    '@graph'   => $this->graph
                ];
                echo '<script type="application/ld+json">' . wp_json_encode( $encode_args, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>' . "\n";
            }

            /**
             * Get custom logo urldecode
             * 
             * @since 1.0.0
             */
            private function get_custom_logo_url() {
                $custom_logo_id = get_theme_mod( 'custom_logo' );
                if( $custom_logo_id ) {
                    $image = wp_get_attachment_image_src( $custom_logo_id , 'full' );
                    if( $image ) {
                        return $image[ 0 ];
                    }
                }
                return '';
            }
            
            /**
             * Site schema
             * Prepare schema content of the website and organization.
             * This schema will generate for each page.
             * 
             * @since 1.0.0
             */
            private function schema_site() {
                $website_schema = [
                    '@type'           => 'WebSite',
                    '@id'             => home_url( '/#website' ),
                    'url'             => home_url( '/'),
                    'name'            => get_bloginfo( 'name' ),
                    'inLanguage'      => get_bloginfo( 'language' ),
                    'potentialAction' => [
                        '@type'       => 'SearchAction',
                        'target'      => [
                            '@type'       => 'EntryPoint',
                            'urlTemplate' => home_url( '/?s={search_term_string}' )
                        ],
                        'query-input' => 'required name=search_term_string'
                    ]
                ];

                $description = get_bloginfo( 'description' );
                if( $description ) {
                    $website_schema[ 'description' ] = $description;
                }

                $organization_schema = [
                    '@type'  => 'Organization',
                    '@id'    => home_url( '/#organization' ),
                    'name'   => get_bloginfo( 'name' ),
                    'url'    => home_url( '/' ),
                ];

                $logo_url = $this->get_custom_logo_url();
                if( $logo_url ) {
                    $organization_schema[ 'logo' ] =  [
                        '@type' => 'ImageObject',
                        '@id'   => home_url( '/#logo') ,
                        'url'   => $logo_url
                    ];
                }

                $graph = [
                    $website_schema,
                    $organization_schema
                ];

                $this->graph = array_merge( $this->graph, $graph );
            }

            /**
             * Schame Post
             * Prepare schema for single post.
             * This schema will be generated for single post only and only when inside
             * a single posts page.
             * 
             * @since 1.0.o
             */
            function schema_post() {
                if ( ! is_single() ) return;

                $author_id = get_post_field( 'post_author', get_the_ID() );
                $featured_image = get_the_post_thumbnail_url();
                $author_image = get_avatar_url( $author_id, [ 'size' => 96 ] );

                $post_schema = [
                    '@type'           => 'BlogPosting',
                    '@id'             => get_permalink() . '#post',
                    'headline'        => get_the_title(),
                    'description'     => get_the_excerpt(),
                    'datePublished'   => get_the_date( 'c' ),
                    'dateModified'    => get_the_modified_date( 'c' ),
                    'inLanguage'      => get_bloginfo( 'language' ),
                    'url'             => get_permalink(),
                    'author'          => [ '@id' => home_url( '/#author-' . $author_id ) ],
                    'publisher'       => [ '@id' => home_url( '/#organization' ) ],
                    'isPartOf'        => [ '@id' => home_url( '/#website' ) ]
                ];

                if( $featured_image ) {
                    $post_schema[ 'image' ] = [
                        '@type' =>  'ImageObject',
                        'url'   =>  $featured_image
                    ];
                }

                $author_schema = [
                    '@type'  => 'Person',
                    '@id'    => home_url( '/#author-' . $author_id ),
                    'name'   => get_the_author_meta( 'display_name', $author_id ),
                    'url'    => get_author_posts_url( $author_id )
                ];

                if( $author_image ) {
                    $author_schema[ 'image' ] = [
                        '@type' => 'ImageObject',
                        'url'   => $author_image
                    ];
                }

                $graph = [
                    $post_schema,
                    $author_schema
                ];

                $this->graph = array_merge( $this->graph, $graph );
            }

            /**
             * Archive schema
             * 
             * @since 1.0.0
             */
            private function schema_archive() {
                if ( ! is_home() && ! is_archive() ) return;

                $schema = [
                    '@type'   => 'CollectionPage',
                    '@id'     => get_pagenum_link( get_query_var( 'paged' ) ),
                    'url'     => get_pagenum_link( get_query_var( 'paged' ) ),
                    'isPartOf' => [ '@id' => home_url( '/#website' ) ],
                    'inLanguage' => get_bloginfo( 'language' ),
                ];

                // name and description vary by archive type
                if ( is_home() || is_front_page() ) {
                    $schema[ 'name' ] = get_bloginfo( 'name' );
                    $description    = get_bloginfo( 'description' );
                    if ( $description ) {
                        $schema[ 'description' ] = $description;
                    }

                } elseif ( is_category() ) {
                    $schema[ 'name' ]        = single_cat_title( '', false );
                    $schema[ 'description' ] = category_description();

                } elseif ( is_tag() ) {
                    $schema[ 'name' ]        = single_tag_title( '', false );
                    $schema[ 'description' ] = tag_description();

                } elseif ( is_author() ) {
                    $schema[ '@t ype'] = 'ProfilePage'; // more specific than CollectionPage
                    $schema[ 'name' ]  = get_the_author_meta( 'display_name', get_queried_object_id() );

                } elseif ( is_date() ) {
                    $schema[ 'name' ] = get_the_archive_title();
                }

                // attach only posts that are actually rendered on this page
                if ( ! empty( Query_Manager::get_post_ids() ) ) {
                    $schema[ 'hasPart' ] = array_map( [ $this, 'post_map' ], Query_Manager::get_post_ids() );
                    $this->author_map();
                }

                $this->graph = array_merge( $this->graph, [ $schema ] );
            }

            /**
             * Post Map
             * 
             * @param int $post The current looping post
             * @since 1.0.0
             */
            public function post_map( $post ) {
                $featured_image = get_the_post_thumbnail_url( $post );

                $part = [
                    '@type'           => 'BlogPosting',
                    '@id'             => get_permalink( $post ),
                    'url'             => get_permalink( $post ),
                    'name'            => get_the_title( $post ),
                    'datePublished'   => get_the_date( 'c', $post ),
                    'dateModified'    => get_the_modified_date( 'c', $post ),
                    'author'          => [ '@id' => home_url( '/#author-' . get_post_field( 'post_author', $post ) ) ],
                ];
                if( $featured_image ) {
                    $part[ 'image' ] = [
                        '@type' =>  'ImageObject',
                        'url'   =>  $featured_image
                    ];
                }
                return $part;
            }

            /**
             * Author Map
             * 
             * @since 1.0.0
             */
            public function author_map() {
                // add each author as a Person entity at top level
                $author_ids = array_unique(
                    array_map( fn( $id ) => get_post_field( 'post_author', $id ), Query_Manager::get_post_ids() )
                );
                foreach ( $author_ids as $author_id ) {
                    $author_schema = [
                        '@type' => 'Person',
                        '@id'   => home_url( '/#author-' . $author_id ),
                        'name'  => get_the_author_meta( 'display_name', $author_id ),
                        'url'   => get_author_posts_url( $author_id ),
                    ];

                    $avatar = get_avatar_url( $author_id, [ 'size' => 96 ] );
                    if ( $avatar ) {
                        $author_schema[ 'image' ] = [
                            '@type' => 'ImageObject',
                            'url'   => $avatar
                        ];
                    }

                    $this->graph[] = $author_schema;
                }
            }

            /**
             * Page schema
             * 
             * @since 1.0.0
             */
            private function schema_page() {
                if ( ! is_page() ) return;

                $schema = [
                    '@type'       => 'WebPage',
                    '@id'         => get_permalink() . '#webpage',
                    'url'         => get_permalink(),
                    'name'        => get_the_title(),
                    'isPartOf'    => [ '@id' => home_url( '/#website' ) ],
                    'inLanguage'  => get_bloginfo( 'language' ),
                    'datePublished' => get_the_date( 'c' ),
                    'dateModified'  => get_the_modified_date( 'c' ),
                ];

                $description = get_the_excerpt();
                if ( $description && has_excerpt() ) {
                    $schema[ 'description' ] = $description;
                }

                $this->graph = array_merge( $this->graph, [ $schema ] );
            }

            /**
             * Search Page schema
             * 
             * @since 1.0.0
             */
            private function schema_search() {
                if ( ! is_search() ) return;

                $schema = [
                    '@type'      => 'SearchResultsPage',
                    '@id'        => get_search_link( get_search_query() ),
                    'url'        => get_search_link( get_search_query() ),
                    'name'       => sprintf( __( 'Search results for: %s', 'blog-bee' ), get_search_query() ),
                    'isPartOf'   => [ '@id' => home_url( '/#website' ) ],
                    'inLanguage' => get_bloginfo( 'language' ),
                ];

                $this->graph = array_merge( $this->graph, [ $schema ] );
            }

            /**
             * 404 schema
             * 
             * @since 1.0.0
             */
            private function schema_404() {
                if ( ! is_404() ) return;

                $schema = [
                    '@type'    => 'WebPage',
                    '@id'      => home_url( '/404' ),
                    'url'      => home_url( '/404' ),
                    'name'     => __( 'Page Not Found', 'blog-bee' ),
                    'isPartOf' => [ '@id' => home_url( '/#website' ) ],
                ];

                $this->graph = array_merge( $this->graph, [ $schema ] );
            }
        }
        new Schema_Manager();
    }