<?php

/**
 * Taken from https://github.com/palmiak/timber-acf-wp-blocks and modified as needed.
 */

namespace Silvanus\includes\blocks;

use apexl\Config\Configuration;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use Timber\Timber;

use function locate_template;

if (! class_exists('TimberAcfWpBlocks')) {
    class TimberAcfWpBlocks
    {
        public function __construct() {}

        public static function timber_block_init(): void
        {
            $acfFields = Configuration::getInstance();

            // Load options pages: child first via locate_template()
            $optionFiles = [
                'src/includes/options/footer-settings.acf.php',
                'src/includes/options/site-settings.acf.php',
                'src/includes/options/theme-settings.acf.php',
            ];

            foreach ($optionFiles as $rel) {
                $found = locate_template($rel);
                if ($found && is_readable($found)) {
                    $acfFields::load($found);
                }
            }

            // Scan both child + parent blocks directories (child wins).
            $blockRoots = [
                trailingslashit(get_stylesheet_directory()) . '/blocks',
                trailingslashit(get_template_directory()) . '/blocks',
            ];

            $twigBySlug = [];
            $acfBySlug = [];

            foreach ($blockRoots as $root) {
                if (!is_dir($root) || !is_readable($root)) {
                    continue;
                }

                foreach (self::scanFilesBySlug($root, 'twig') as $slug => $path) {
                    if (!isset($twigBySlug[$slug])) {
                        $twigBySlug[$slug] = $path;
                    }
                }

                // Prefer PHP ACF definitions over JSON if both exist; child still wins overall.
                foreach (self::scanFilesBySlug($root, 'acf.php') as $slug => $path) {
                    if (!isset($acfBySlug[$slug])) {
                        $acfBySlug[$slug] = $path;
                    }
                }
                foreach (self::scanFilesBySlug($root, 'acf.json') as $slug => $path) {
                    if (!isset($acfBySlug[$slug])) {
                        $acfBySlug[$slug] = $path;
                    }
                }
            }

            foreach ($twigBySlug as $slug => $twigPath) {
                $file_headers = get_file_data(
                    $twigPath,
                    [
                        'title' => 'Title',
                        'description' => 'Description',
                        'category' => 'Category',
                        'icon' => 'Icon',
                        'keywords' => 'Keywords',
                        'mode' => 'Mode',
                        'align' => 'Align',
                        'post_types' => 'PostTypes',
                        'supports_align' => 'SupportsAlign',
                        'supports_align_content' => 'SupportsAlignContent',
                        'supports_mode' => 'SupportsMode',
                        'supports_multiple' => 'SupportsMultiple',
                        'supports_anchor' => 'SupportsAnchor',
                        'enqueue_style' => 'EnqueueStyle',
                        'enqueue_script' => 'EnqueueScript',
                        'enqueue_assets' => 'EnqueueAssets',
                        'supports_custom_class_name' => 'SupportsCustomClassName',
                        'supports_reusable' => 'SupportsReusable',
                        'supports_full_height' => 'SupportsFullHeight',
                        'example' => 'Example',
                        'supports_jsx' => 'SupportsJSX',
                        'parent' => 'Parent',
                        'default_data' => 'DefaultData',
                    ],
                );

                // Older block headers wrap some values in pipes (e.g. "Title: | Title & Text |").
                // Strip the leading/trailing pipes + whitespace so those blocks register cleanly;
                // values that never had pipes are left untouched.
                foreach (['title', 'description', 'category', 'icon', 'align', 'supports_align'] as $pipedField) {
                    if (isset($file_headers[$pipedField]) && is_string($file_headers[$pipedField])) {
                        $file_headers[$pipedField] = trim($file_headers[$pipedField], " |\t");
                    }
                }

                if (empty($file_headers['title']) || empty($file_headers['category'])) {
                    continue;
                }

                // Load ACF field config for this block slug (child overrides parent).
                if (isset($acfBySlug[$slug]) && is_readable($acfBySlug[$slug])) {
                    $acfFields::load($acfBySlug[$slug]);
                }

                $keywords = str_getcsv($file_headers['keywords'], ' ', '"', "\\");

                $data = [
                    'name' => $slug,
                    'title' => $file_headers['title'],
                    'description' => $file_headers['description'],
                    'category' => $file_headers['category'],
                    'icon' => $file_headers['icon'],
                    'keywords' => $keywords,
                    'mode' => $file_headers['mode'],
                    'align' => $file_headers['align'],
                    'render_callback' => [__CLASS__, 'timber_blocks_callback'],
                    'enqueue_assets' => $file_headers['enqueue_assets'],
                    'default_data' => $file_headers['default_data'],
                ];

                $data = array_filter($data);

                if (! empty($file_headers['post_types'])) {
                    $data['post_types'] = explode(' ', $file_headers['post_types']);
                }

                // Ensure ACF sees supports.jsx=true when SupportsJSX: true is in the Twig header
                $supportsJsx = strtolower(trim((string) ($file_headers['supports_jsx'] ?? '')));
                if (in_array($supportsJsx, ['1', 'true', 'yes', 'on'], true)) {
                    $data['supports'] = $data['supports'] ?? [];
                    $data['supports']['jsx'] = true;
                }

                $data = self::timber_block_default_data($data);

                acf_register_block_type($data);
            }

            if (! empty($groups = $acfFields::getData())) {
                foreach ($groups as $group) {
                    acf_add_local_field_group($group);
                }
            }
        }

        /**
         * Scan a blocks root directory recursively for a given suffix and return slug => fullPath.
         *
         * Supported $type:
         * - 'twig'     => slug.twig
         * - 'acf.php'  => slug.acf.php
         * - 'acf.json' => slug.acf.json
         *
         * @return array<string,string>
         */
        private static function scanFilesBySlug(string $root, string $type): array
        {
            $map = [];

            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::LEAVES_ONLY,
            );

            foreach ($iterator as $fileInfo) {
                if (!$fileInfo->isFile()) {
                    continue;
                }

                $filename = $fileInfo->getFilename();
                $realPath = $fileInfo->getRealPath();

                if (!is_string($filename) || !is_string($realPath) || $realPath === '') {
                    continue;
                }

                $slug = null;

                if ($type === 'twig' && str_ends_with($filename, '.twig')) {
                    $slug = $fileInfo->getBasename('.twig');
                } elseif ($type === 'acf.php' && str_ends_with($filename, '.acf.php')) {
                    $slug = preg_replace('/\.acf\.php$/', '', $filename);
                } elseif ($type === 'acf.json' && str_ends_with($filename, '.acf.json')) {
                    $slug = preg_replace('/\.acf\.json$/', '', $filename);
                }

                if (is_string($slug) && $slug !== '') {
                    $map[$slug] = $realPath;
                }
            }

            return $map;
        }

        /**
         * Callback to register blocks
         *
         * @param  array  $block  stores all the data from ACF.
         * @param  string  $content  content passed to block.
         * @param  bool  $is_preview  checks if block is in preview mode.
         * @param  int  $post_id  Post ID.
         */
        public static function timber_blocks_callback(array $block, string $content = '', bool $is_preview = false, int $post_id = 0): void
        {
            $context = Timber::context();

            $slug = str_replace('acf/', '', $block['name']);

            /**
             * IMPORTANT:
             * For ACF InnerBlocks to be editable, ACF needs to see <InnerBlocks /> in a JSX-capable template.
             * Twig server-rendering won't mount InnerBlocks in the editor, so we special-case editor preview.
             */
            if (is_admin() && $slug === 'content-container') {
                $alignClass = !empty($block['align']) ? 'align' . $block['align'] : '';
                $extraClass = !empty($block['className']) ? (string) $block['className'] : '';

                echo '<div class="content-container content-container--editor ' . esc_attr(trim($alignClass . ' ' . $extraClass)) . '">';
                echo '  <div class="content content-wrapper content-container__editor-inner">';
                echo '    <div class="content-container__placeholder" aria-hidden="true">';
                echo '      <strong>Content Container</strong><br>';
                echo '      Drop blocks here, or click “+” to insert.';
                echo '    </div>';
                echo '    <InnerBlocks />';
                echo '  </div>';
                echo '</div>';

                return;
            }

            if (is_admin() && $slug === 'columns') {
                $alignClass = !empty($block['align']) ? 'align' . $block['align'] : '';
                $extraClass = !empty($block['className']) ? (string) $block['className'] : '';

                echo '<div class="columns columns--editor ' . esc_attr(trim($alignClass . ' ' . $extraClass)) . '">';
                echo '  <div class="content content-wrapper columns__editor-inner">';
                echo '    <div class="columns__placeholder" aria-hidden="true">';
                echo '      <strong>Columns</strong><br>';
                echo '      Add single column blocks here.';
                echo '    </div>';
                echo '    <InnerBlocks />';
                echo '  </div>';
                echo '</div>';

                return;
            }

            if (is_admin() && $slug === 'column-single') {
                $alignClass = !empty($block['align']) ? 'align' . $block['align'] : '';
                $extraClass = !empty($block['className']) ? (string) $block['className'] : '';

                echo '<div class="columns columns--editor ' . esc_attr(trim($alignClass . ' ' . $extraClass)) . '">';
                echo '  <div class="content content-wrapper columns__editor-inner">';
                echo '    <div class="columns__placeholder" aria-hidden="true">';
                echo '      <strong>Single Column</strong><br>';
                echo '      Add blocks here.';
                echo '    </div>';
                echo '    <InnerBlocks />';
                echo '  </div>';
                echo '</div>';

                return;
            }

            $context['block'] = $block;
            $context['post_id'] = $post_id;
            $context['slug'] = $slug;
            $context['is_preview'] = $is_preview;
            $context['fields'] = \get_fields();

            // Pass inner block content through to Twig for frontend rendering.
            $context['content'] = $content;

            // ... existing code building classes + rendering ...
            $classes = array_merge(
                [$slug],
                isset($block['className']) ? [$block['className']] : [],
                $is_preview ? ['is-preview'] : [],
                ['align' . $context['block']['align']],
            );

            $context['classes'] = implode(' ', $classes);

            $is_example = false;

            if (! empty($block['data']['is_example'])) {
                $is_example = true;
                $context['fields'] = $block['data'];
            }

            $context = apply_filters('timber/acf-gutenberg-blocks-data', $context);
            $context = apply_filters('timber/acf-gutenberg-blocks-data/' . $slug, $context);
            $context = apply_filters('timber/acf-gutenberg-blocks-data/' . $block['id'], $context);

            $paths = self::timber_acf_path_render($slug, $is_preview, $is_example);
            Timber::render($paths, $context);
        }

        /**
         * Generates an array with paths and slugs
         *
         * @param  string  $slug  File slug.
         * @param  bool  $is_preview  Checks if is a preview.
         * @param  bool  $is_example  Checks if is an example.
         */
        public static function timber_acf_path_render(string $slug, bool $is_preview, bool $is_example): array
        {
            $directories = self::timber_block_directory_getter();

            $ret = [];

            /**
             * Filters the name of suffix for an example file.
             *
             * @since 1.12
             */
            $example_identifier = apply_filters('timber/acf-gutenberg-blocks-example-identifier', '-example');

            /**
             * Filters the name of suffix for a preview file.
             *
             * @since 1.12
             */
            $preview_identifier = apply_filters('timber/acf-gutenberg-blocks-preview-identifier', '-preview');

            foreach ($directories as $directory) {
                if ($is_example) {
                    $twigFile_example = $directory . "/{$slug}{$example_identifier}.twig";
                    if (file_exists(locate_template($twigFile_example))) {
                        $ret[] = $twigFile_example;
                    }
                }
                if ($is_preview) {
                    $twigFile_preview = $directory . "/{$slug}{$preview_identifier}.twig";
                    if (file_exists(locate_template($twigFile_preview))) {
                        $ret[] = $twigFile_preview;
                    }
                }
                $twigFile = $directory . "/{$slug}.twig";
                if (file_exists(locate_template($twigFile))) {
                    $ret[] = $twigFile;
                }
            }

            return $ret;
        }

        /**
         * Generates the list of subfolders based on current directories
         *
         * @param array $directories  Filepath array.
         */
        public static function timber_blocks_subdirectories(array $directories): array
        {
            $ret = [];

            foreach ($directories as $base_directory) {
                // Check if the folder exists in the child, then parent if we haven't already checked this.
                $themeDirs = [
                    trailingslashit(get_template_directory()) . '/'.$base_directory,
                    trailingslashit(get_stylesheet_directory()) . '/'.$base_directory,
                ];

                foreach($themeDirs as $themeDir) {
                    if (!file_exists($themeDir)) {
                        continue;
                    }

                    $template_directory = new RecursiveDirectoryIterator(
                        $themeDir,
                        FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_SELF,
                    );

                    foreach ($template_directory as $directory) {
                        if ($directory->isDir() && !$directory->isDot()) {
                            $ret[] = $found_dir = $base_directory . '/' . $directory->getFilename();

                            $sub = self::timber_blocks_subdirectories([$found_dir]);
                            if (!empty($sub)) {
                                $ret = array_merge($ret, $sub);
                            }
                        }
                    }
                }
            }

            return $ret;
        }

        /**
         * Universal function to handle getting folders and subfolders
         */
        public static function timber_block_directory_getter()
        {
            // Get an array of directories containing blocks.
            $directories = apply_filters('timber/acf-gutenberg-blocks-templates', ['blocks']);

            // Check subfolders.
            $subdirectories = self::timber_blocks_subdirectories($directories);

            if (! empty($subdirectories)) {
                $directories = array_merge($directories, $subdirectories);
            }

            return $directories;
        }

        /**
         * Default options setter.
         *
         * @param  [array] $data - header set data.
         * @return array|mixed [array]
         */
        public static function timber_block_default_data($data): mixed
        {
            $default_data = apply_filters('timber/acf-gutenberg-blocks-default-data', []);
            $data_array = [];

            if (! empty($data['default_data'])) {
                $default_data_key = $data['default_data'];
            }

            if (isset($default_data_key) && ! empty($default_data[$default_data_key])) {
                $data_array = $default_data[$default_data_key];
            } elseif (! empty($default_data['default'])) {
                $data_array = $default_data['default'];
            }

            if (is_array($data_array)) {
                $data = array_merge($data_array, $data);
            }

            return $data;
        }
    }
}
