<?php
/**
 * Loads and normalizes the editor registry (JSON + filter `fleekdash_editor_registry`).
 *
 * @package FleekDash\Services
 */

namespace FleekDash\Services;

if (!defined('ABSPATH')) {
    exit;
}

final class EditorSettingsRegistry
{
    /**
     * @var array<string, array<string, mixed>>|null
     */
    private static $definitionsCache = null;

    /**
     * Drop cached registry (e.g. after tests change `fleekdash_editor_registry` filters).
     */
    public static function clearDefinitionsCache(): void
    {
        self::$definitionsCache = null;
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    public static function getDefinitions(): array
    {
        if (self::$definitionsCache === null) {
            $editors = self::parseRegistryEditorsFromDisk();
            $filtered = \apply_filters('fleekdash_editor_registry', $editors);
            self::$definitionsCache = self::normalizeEditorRegistryFromFilter($filtered, $editors);
        }

        return self::$definitionsCache;
    }

    /**
     * Compact tuples for detection (id => [name, template, primary plugin_pattern, builtin, plugin_patterns]).
     *
     * @return array<string, array{0: string, 1: string, 2: string|null, 3: bool, 4: list<string>}>
     */
    public static function getTuples(): array
    {
        $tuples = [];
        foreach (self::getDefinitions() as $id => $def) {
            $row = self::editorDefinitionToTuple((string) $id, $def);
            if ($row !== null) {
                $tuples[$id] = $row;
            }
        }

        return $tuples;
    }

    /**
     * @param mixed $def
     * @return array{0: string, 1: string, 2: string|null, 3: bool, 4: list<string>}|null
     */
    private static function editorDefinitionToTuple(string $id, $def): ?array
    {
        if (!\is_array($def)) {
            return null;
        }
        /** @var array<string, mixed> $def */
        $name = isset($def['name']) ? (string) $def['name'] : $id;
        $tpl = isset($def['edit_url_template']) ? (string) $def['edit_url_template'] : '';
        $patterns = self::collectPluginPatternsFromDefinition($def);
        $primary = $patterns[0] ?? null;
        $builtin = !empty($def['builtin']);

        return [$name, $tpl, $primary, $builtin, $patterns];
    }

    /**
     * @param array<string, mixed> $def
     * @return list<string>
     */
    private static function collectPluginPatternsFromDefinition(array $def): array
    {
        $patterns = [];
        if (!empty($def['plugin_pattern']) && \is_string($def['plugin_pattern'])) {
            $patterns[] = $def['plugin_pattern'];
        }
        $extras = $def['plugin_patterns'] ?? null;
        if (!\is_array($extras)) {
            return $patterns;
        }
        foreach ($extras as $extra) {
            if (\is_string($extra) && $extra !== '' && !\in_array($extra, $patterns, true)) {
                $patterns[] = $extra;
            }
        }

        return $patterns;
    }

    /**
     * @param array{0: string, 1: string, 2: string|null, 3?: bool, 4?: list<string>} $tuple
     * @return array{name: string, builtin: bool, edit_url_template: string, plugin_pattern: string|null, plugin_patterns: list<string>, supported_post_types: string[]}
     */
    public static function normalizeEditorConfig(array $tuple): array
    {
        $patterns = [];
        if (isset($tuple[4]) && \is_array($tuple[4])) {
            foreach ($tuple[4] as $p) {
                if (\is_string($p) && $p !== '') {
                    $patterns[] = $p;
                }
            }
        }
        if ($patterns === [] && isset($tuple[2]) && \is_string($tuple[2]) && $tuple[2] !== '') {
            $patterns = [$tuple[2]];
        }

        return [
            'name' => $tuple[0],
            'builtin' => $tuple[3] ?? false,
            'edit_url_template' => $tuple[1],
            'plugin_pattern' => $tuple[2] ?? null,
            'plugin_patterns' => $patterns,
            'supported_post_types' => ['*'],
        ];
    }

    /**
     * @param mixed $filtered
     * @param array<string, array<string, mixed>> $fallback
     * @return array<string, array<string, mixed>>
     */
    private static function normalizeEditorRegistryFromFilter($filtered, array $fallback): array
    {
        if (!\is_array($filtered)) {
            return $fallback;
        }
        $out = [];
        foreach ($filtered as $id => $def) {
            $key = self::registryEntryKey($id);
            if ($key === '' || !\is_array($def)) {
                continue;
            }
            $out[$key] = $def;
        }

        return $out !== [] ? $out : $fallback;
    }

    /**
     * @param mixed $id
     */
    private static function registryEntryKey($id): string
    {
        if (\is_string($id)) {
            return $id;
        }
        if (\is_int($id)) {
            return (string) $id;
        }

        return '';
    }

    /**
     * Read editors-registry.json from disk or fall back to minimal built-in registry.
     *
     * @return array<string, array<string, mixed>>
     */
    private static function parseRegistryEditorsFromDisk(): array
    {
        $editors = self::minimalRegistryEditors();
        $base = \defined('FLEEKDASH_PLUGIN_DIR')
            ? FLEEKDASH_PLUGIN_DIR
            : \dirname(__DIR__, 2) . \DIRECTORY_SEPARATOR;
        $path = $base . 'data' . \DIRECTORY_SEPARATOR . 'editors-registry.json';
        if (!\is_readable($path)) {
            return $editors;
        }
        $raw = \file_get_contents($path);
        if ($raw === false || $raw === '') {
            return $editors;
        }
        $decoded = \json_decode($raw, true);
        $valid = \is_array($decoded)
            && !empty($decoded['editors'])
            && \is_array($decoded['editors']);
        if ($valid) {
            $editors = $decoded['editors'];
        }

        return $editors;
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private static function minimalRegistryEditors(): array
    {
        return [
            'gutenberg' => [
                'name' => 'Gutenberg',
                'edit_url_template' => '/wp-admin/post.php?post={post_id}&action=edit',
                'plugin_pattern' => null,
                'builtin' => true,
                'is_frontend' => false,
                'install_url' => null,
            ],
        ];
    }
}
