<?php

namespace SEOAIC;

class SeoaicTemplates
{
    public function __construct()
    {
        $this->init();
    }

    private function init()
    {
        if (
            SEOAIC_SETTINGS::isCPTEnabled()
            && SEOAIC_SETTINGS::isCustomTemplateEnabled()
        ) {
            // add_action('init', [$this, 'registerPluginTemplates']);
            // add_filter('template_include', [$this, 'seoaiTemplateInclude'], 99);
            add_filter('template_include', [$this, 'seoaiTemplateInclude2'], 99);
        }

        // add_filter('pre_get_block_template', [$this, 'get_block_template_fallback'], 20, 3);
    }

    function seoaiTemplateInclude2($template)
    {
        global $post;

        if (SEOAIC_SETTINGS::SEOAI_POST != $post->post_type) {
            return $template;
        }

        $file = $this->getTemplatesDir() . 'single-seoai-classic.php';

        if (file_exists($file)) {
            return $file;
        }

        return $template;
    }

    public function getDefaultContent()
    {
        $file = $this->getTemplatesDir() . '/parts/content.html';

        if (file_exists($file)) {
            return file_get_contents($file);
        }

        return '';
    }

    public function getDefaultCSS()
    {
        $file = $this->getTemplatesDir() . '/parts/content.css';

        if (file_exists($file)) {
            return file_get_contents($file);
        }

        return '';
    }














    public function registerPluginTemplates()
    {
        if (function_exists('register_block_template')) { // for WP >= 6.7.0
            register_block_template('seoai-client//single-seoai', [
                'title'       => __('WP SEO AI single post', 'seoaic'),
                'description' => __('Template used to display the single WP SEO AI post.', 'seoaic'),
                'content'     => $this->getTemplateContent('single-seoai.php'),
            ]);

        } else {
            add_filter('pre_get_block_file_template', [$this, 'getBlockFileTemplate'], 20, 3);
            add_filter('get_block_templates', [$this, 'addBlockTemplates'], 20, 3);
        }
    }

    function getTemplateContent(string $templateFilename)
    {
        $file = $this->getTemplatesDir() . $templateFilename;
        ob_start();

        if (file_exists($file)) {
            include $file;
        }

        return ob_get_clean();
    }



    function seoaiTemplateInclude($template)
    {
        global $post;

        if (SEOAIC_SETTINGS::SEOAI_POST != $post->post_type) {
            return $template;
        }

        $templates = [
            'single-seoai.php',
            'single.php',
            'index.php',
        ];

        // First, search for PHP templates, which block themes can also use.
        $template = locate_template($templates);

        // Pass the result into the block template locator and let it figure
        // out whether block templates are supported and this template exists.
        $template = locate_block_template($template, 'single-seoai', $templates);

        return $template;
    }

    private function getBlockTemplateTypes()
    {
        $templateTypes = [
            'single-seoai' => [
                'title'         => __('WP SEO AI single post', 'seoaic'),
                'description'   => __('Template used to display the single WP SEO AI post.', 'seoaic'),
            ],
        ];

        return $templateTypes;
    }

    private function getTemplatesDir()
    {
        $directory = SEOAIC_DIR . 'inc/templates/';

        return $directory;
    }

    public function getBlockFileTemplate($template, $id, $templateType)
    {
        $templateNameParts = explode('//', $id);

        if (count($templateNameParts) < 2) {
            return $template;
        }

        list($templateId, $templateSlug) = $templateNameParts;

        // Not ours
        if (seoaiGetPluginSlug(true) !== $templateId) {
            return $template;
        }

        $templateFilePath = $this->getTemplatesDir() . $templateSlug . '.php';

        $templateObject = $this->createNewBlockTemplateObject($templateFilePath, $templateType, $templateSlug);

        $templateBuilt = $this->buildTemplateFromFile($templateObject, $templateType);

        if (null !== $templateBuilt) {
            return $templateBuilt;
        }

        return $template;
    }

    private function getDBBlockTemplates($slugs = [], $templateType = 'wp_template')
    {
        $args = [
            'post_type'         => $templateType,
            'posts_per_page'    => -1,
            'no_found_rows'     => true,
            'skip_hiding'       => true,
            'tax_query'         => [  // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
                [
                    'taxonomy'  => 'wp_theme',
                    'field'     => 'name',
                    'terms'     => [seoaiGetPluginSlug(true), get_stylesheet()],
                ],
            ],
        ];

        if (
            is_array($slugs)
            && count($slugs) > 0) {
            $args['post_name__in'] = $slugs;
        }

        $query = new \WP_Query($args);
        $savedTemplates = $query->posts;

        return array_map(
            function ($savedTemplate) {
                return $this->buildTemplateFromPost($savedTemplate);
            },
            $savedTemplates
        );
    }

    private static function buildTemplateFromPost($post)
    {
        $terms = get_the_terms($post, 'wp_theme');

        if (is_wp_error($terms)) {
            return $terms;
        }

        if (!$terms) {
            return new \WP_Error('template_missing_theme', __('No theme is defined for this template.', 'seoaic'));
        }

        $theme = $terms[0]->name;

        $template = new \WP_Block_Template();
        $template->wp_id            = $post->ID;
        $template->id               = $theme . '//' . $post->post_name;
        $template->theme            = $theme;
        $template->content          = $post->post_content;
        $template->slug             = $post->post_name;
        $template->source           = 'custom';
        $template->type             = $post->post_type;
        $template->description      = $post->post_excerpt;
        $template->title            = $post->post_title;
        $template->status           = $post->post_status;
        $template->has_theme_file   = true;
        $template->is_custom        = false;
        $template->post_types       = []; // Don't appear in any Edit Post template selector dropdown.

        if ('wp_template_part' === $post->post_type) {
            $typeTerms = get_the_terms($post, 'wp_template_part_area');

            if (
                !is_wp_error($typeTerms)
                && false !== $typeTerms
            ) {
                $template->area = $typeTerms[0]->name;
            }
        }

        // We are checking 'woocommerce' to maintain classic templates which are saved to the DB,
        // prior to updating to use the correct slug.
        // More information found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423.
        if (seoaiGetPluginSlug(true) === $theme) {
            $template->origin = 'plugin';
        }

        /*
        * Run the block hooks algorithm introduced in WP 6.4 on the template content.
        */
        if (function_exists('inject_ignored_hooked_blocks_metadata_attributes')) {
            $hookedBlocks = get_hooked_blocks();

            if (
                !empty($hookedBlocks)
                || has_filter('hooked_block_types')
            ) {
                $beforeBlockVisitor = make_before_block_visitor($hookedBlocks, $template);
                $afterBlockVisitor = make_after_block_visitor($hookedBlocks, $template);
                $blocks = parse_blocks($template->content);
                $template->content = traverse_and_serialize_blocks($blocks, $beforeBlockVisitor, $afterBlockVisitor);
            }
        }

        return $template;
    }

    private function getBlockTemplates($slugs = [], $templateType = 'wp_template')
    {
        $dbTemplates = $this->getDBBlockTemplates($slugs, $templateType);
        $templates = $this->getFilesBlockTemplates($slugs, $dbTemplates, $templateType);

        return array_merge($dbTemplates, $templates);
    }

    private function getFilesBlockTemplates($slugs, $dbTemplates, $templateType): array
    {
        $filesTemplates = $this->getTemplatesFiles($templateType);
        $templates = [];

        foreach ($filesTemplates as $templateFile) {
            $templateSlug = basename($templateFile, ".php");

            // Skip template which has no slug we're looking for.
            if (
                is_array($slugs)
                && count($slugs) > 0
                && !in_array($templateSlug, $slugs, true)
            ) {
                continue;
            }

            $filtered = array_filter(
                $dbTemplates,
                function ($template) use ($templateSlug) {
                    $templateObj = (object)$template; //phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
                    return $templateObj->slug === $templateSlug;
                }
            );
            if (count($filtered) > 0) {
                continue;
            }

            // At this point the template only exists in the Blocks filesystem and has not been saved in the DB,
            // or superseded by the theme.
            $templateObject = $this->createNewBlockTemplateObject($templateFile, $templateType, $templateSlug);
            $templates[] = $templateObject;
        }

        return $templates;
    }

    public function addBlockTemplates($queryResult, $query, $templateType)
    {
        $postType = isset($query['post_type']) ? $query['post_type'] : '';
        $slugs = isset($query['slug__in']) ? $query['slug__in'] : [];
        $templateFiles = $this->getBlockTemplates($slugs, $templateType);

        foreach ($templateFiles as $templateFile) {
            if (
                $postType
                && isset($templateFile->post_types)
                && !in_array($postType, $templateFile->post_types, true)
            ) {
                continue;
            }

            if ('custom' !== $templateFile->source) {
                $template = $this->buildTemplateFromFile($templateFile, $templateType);

            } else {
                $templateFile->title = $this->getBlockTemplateTitle($templateFile->slug);

                $templateFile->description = $this->getBlockTemplateDescription($templateFile->slug);
                $queryResult[] = $templateFile;
                continue;
            }

            $isNotCustom = false === array_search(
                wp_get_theme()->get_stylesheet() . '//' . $templateFile->slug,
                array_column($queryResult, 'id'),
                true
            );
            $fitsSlugQuery = !isset($query['slug__in']) || in_array($templateFile->slug, $query['slug__in'], true);
            $fitsAreaQuery = !isset($query['area']) || $templateFile->area === $query['area'];

            if (
                $isNotCustom
                && $fitsSlugQuery
                && $fitsAreaQuery
            ) {
                $queryResult[] = $template;
            }
        }

        $queryResult = array_map(
            function ($template) {
                if ('theme' === $template->origin) {
                    return $template;
                }

                if ($template->title === $template->slug) {
                    $template->title = $this->getBlockTemplateTitle($template->slug);
                }

                if (!$template->description) {
                    $template->description = $this->getBlockTemplateDescription($template->slug);
                }

                return $template;
            },
            $queryResult
        );

        return $queryResult;
    }

    private function getTemplatesFiles($templateType)
    {
        $templateFiles = $this->getTemplatePaths($this->getTemplatesDir());

        return $templateFiles;
    }

    private function getTemplatePaths($baseDirectory): array
    {
        $paths = [];

        if (file_exists($baseDirectory)) {
            $nestedFiles = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($baseDirectory));
            $nestedHtmlFiles = new \RegexIterator($nestedFiles, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);

            foreach ($nestedHtmlFiles as $path => $file) {
                $paths[] = $path;
            }
        }

        return $paths;
    }

    /**
     * @param string $templateFile Block template file path.
     * @param string $templateType wp_template or wp_template_part.
     * @param string $templateSlug Block template slug e.g. single-seoai.
     * @param bool $templateIsFromTheme If the block template file is being loaded from the current theme instead of blocks.
     *
     * @return object Block template object.
     */
    private function createNewBlockTemplateObject($templateFile, $templateType, $templateSlug, $templateIsFromTheme = false)
    {
        $themeName = wp_get_theme()->get('TextDomain');

        $newTemplateItem = [
            'slug'          => $templateSlug,
            'id'            => $templateIsFromTheme ? $themeName . '//' . $templateSlug : seoaiGetPluginSlug(true) . '//' . $templateSlug,
            'path'          => $templateFile,
            'type'          => $templateType,
            'theme'         => $templateIsFromTheme ? $themeName : seoaiGetPluginSlug(true),
            // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
            'source'        => $templateIsFromTheme ? 'theme' : 'plugin',
            'title'         => $this->slugToTitle($templateSlug),
            'description'   => '',
            'post_types'    => [] // Don't appear in any Edit Post template selector dropdown.
        ];

        return (object)$newTemplateItem;
    }

    private function slugToTitle($templateSlug)
    {
        switch ($templateSlug) {
            case 'single-seoai':
                return esc_html__('WP SEO AI single post', 'seoaic');

            default:
                // Replace all hyphens and underscores with spaces.
                return ucwords(preg_replace('/[\-_]/', ' ', $templateSlug));
        }
    }

    private function buildTemplateFromFile($templateFile, $templateType)
    {
        $templateFile = (object)$templateFile;
        $templateIsFromTheme = 'theme' === $templateFile->source;
        $themeName = wp_get_theme()->get('TextDomain');

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
        $templateContent = file_get_contents($templateFile->path);
        $template = new \WP_Block_Template();
        $template->id               = $templateIsFromTheme ? $themeName . '//' . $templateFile->slug : seoaiGetPluginSlug(true) . '//' . $templateFile->slug;
        $template->theme            = $templateIsFromTheme ? $themeName : seoaiGetPluginSlug(true);
        $template->content          = self::injectThemeAttributeInContent($templateContent);
        $template->source           = $templateFile->source ? $templateFile->source : 'plugin';
        $template->slug             = $templateFile->slug;
        $template->type             = $templateType;
        $template->title            = !empty($templateFile->title) ? $templateFile->title : "";
        $template->description      = !empty($templateFile->description) ? $templateFile->description : "";
        $template->status           = 'publish';
        $template->has_theme_file   = true;
        $template->origin           = $templateFile->source;
        $template->is_custom        = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
        $template->post_types       = []; // Don't appear in any Edit Post template selector dropdown.
        $template->area             = 'uncategorized';

        /*
        * Run the block hooks algorithm introduced in WP 6.4 on the template content.
        */
        if (function_exists('inject_ignored_hooked_blocks_metadata_attributes')) {
            $beforeBlockVisitor = '_inject_theme_attribute_in_template_part_block';
            $afterBlockVisitor = null;
            $hookedBlocks = get_hooked_blocks();

            if (
                !empty($hookedBlocks)
                || has_filter('hooked_block_types')
            ) {
                $beforeBlockVisitor = make_before_block_visitor($hookedBlocks, $template);
                $afterBlockVisitor  = make_after_block_visitor($hookedBlocks, $template);
            }

            $blocks = parse_blocks($template->content);
            $template->content = traverse_and_serialize_blocks($blocks, $beforeBlockVisitor, $afterBlockVisitor);
        }

        return $template;
    }

    private function injectThemeAttributeInContent($templateContent)
    {
        $hasUpdatedContent = false;
        $newContent = '';
        $templateBlocks = parse_blocks($templateContent);
        $blocks = $this->flattenBlocks($templateBlocks);

        foreach ($blocks as &$block) {
            if (
                'core/template-part' === $block['blockName'] &&
                !isset($block['attrs']['theme'])
            ) {
                $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
                $hasUpdatedContent = true;
            }
        }

        if ($hasUpdatedContent) {
            foreach ($templateBlocks as &$block) {
                $newContent .= serialize_block($block);
            }

            return $newContent;
        }

        return $templateContent;
    }

    private function flattenBlocks(&$blocks)
    {
        $allBlocks = [];
        $queue = [];

        foreach ($blocks as &$block) {
            $queue[] = &$block;
        }

        $queueCount = count($queue);

        while ($queueCount > 0) {
            $block = &$queue[0];
            array_shift($queue);
            $allBlocks[] = &$block;

            if (!empty($block['innerBlocks'])) {
                foreach ($block['innerBlocks'] as &$innerBlock) {
                    $queue[] = &$innerBlock;
                }
            }

            $queueCount = count($queue);
        }

        return $allBlocks;
    }

    private function getBlockTemplateTitle($templateSlug)
    {
        $pluginTemplateTypes = $this->getBlockTemplateTypes();

        if (isset($pluginTemplateTypes[$templateSlug])) {
            return $pluginTemplateTypes[$templateSlug]['title'];

        } else {
            // Human friendly title converted from the slug.
            return ucwords(preg_replace('/[\-_]/', ' ', $templateSlug));
        }
    }

    private function getBlockTemplateDescription($templateSlug)
    {
        $templateTypes = $this->getBlockTemplateTypes();

        if (isset($templateTypes[$templateSlug])) {
            return $templateTypes[$templateSlug]['description'];
        }

        return '';
    }
}
