<?php

namespace FleekDash\Services\Theme;

defined('ABSPATH') || exit;

/**
 * ThemeStorage
 *
 * Handles persistent storage for custom themes and palettes using wp_options.
 * Provides cached read/write helpers with basic data validation.
 */
class ThemeStorage
{
    private const OPTION_CUSTOM_THEMES = 'fleekdash_custom_themes';
    private const OPTION_CUSTOM_PALETTES = 'fleekdash_theme_palettes';
    private const OPTION_ACTIVE_THEME = 'fleekdash_active_theme';
    private const CACHE_GROUP = 'fleekdash_theme_service';
    private const CACHE_KEY_THEMES = 'custom_themes';
    private const CACHE_KEY_PALETTES = 'custom_palettes';

    /**
     * Retrieve all custom themes from storage.
     *
     * @return array<string, array<string, mixed>>
     */
    public function getCustomThemes(): array
    {
        $cached = wp_cache_get(self::CACHE_KEY_THEMES, self::CACHE_GROUP);
        if (false !== $cached) {
            return $cached;
        }

        $themes = \get_option(self::OPTION_CUSTOM_THEMES, []);
        if (!is_array($themes)) {
            $themes = [];
        }

        $themes = array_filter(
            array_map([$this, 'sanitizeTheme'], $themes),
            static fn($theme) => is_array($theme) && !empty($theme['id'])
        );

        wp_cache_set(self::CACHE_KEY_THEMES, $themes, self::CACHE_GROUP, HOUR_IN_SECONDS);

        return $themes;
    }

    /**
     * Retrieve a single custom theme by ID.
     */
    public function getCustomTheme(string $themeId): ?array
    {
        $themeId = \sanitize_key($themeId);

        if ($themeId === '') {
            return null;
        }

        $themes = $this->getCustomThemes();

        return $themes[$themeId] ?? null;
    }

    /**
     * Persist a custom theme definition.
     *
     * @param array<string, mixed> $theme
     */
    public function saveCustomTheme(array $theme): void
    {
        $theme = $this->sanitizeTheme($theme);
        $themeId = $theme['id'] ?? '';

        if ($themeId === '') {
            return;
        }

        $themes = $this->getCustomThemes();
        $themes[$themeId] = $theme;

        \update_option(self::OPTION_CUSTOM_THEMES, $themes, false);
        $this->clearThemeCache();
    }

    /**
     * Delete a custom theme by ID.
     */
    public function deleteCustomTheme(string $themeId): void
    {
        $themeId = \sanitize_key($themeId);
        if ($themeId === '') {
            return;
        }

        $themes = $this->getCustomThemes();

        if (!isset($themes[$themeId])) {
            return;
        }

        unset($themes[$themeId]);
        \update_option(self::OPTION_CUSTOM_THEMES, $themes, false);
        $this->clearThemeCache();
    }

    /**
     * Retrieve custom palette definitions.
     *
     * @return array<string, array<string, mixed>>
     */
    public function getCustomPalettes(): array
    {
        $cached = wp_cache_get(self::CACHE_KEY_PALETTES, self::CACHE_GROUP);
        if (false !== $cached) {
            return $cached;
        }

        $palettes = \get_option(self::OPTION_CUSTOM_PALETTES, []);
        if (!is_array($palettes)) {
            $palettes = [];
        }

        $palettes = array_filter(
            array_map([$this, 'sanitizePalette'], $palettes),
            static fn($palette) => is_array($palette) && !empty($palette['id'])
        );

        wp_cache_set(self::CACHE_KEY_PALETTES, $palettes, self::CACHE_GROUP, HOUR_IN_SECONDS);

        return $palettes;
    }

    /**
     * Retrieve a single custom palette by ID.
     */
    public function getCustomPalette(string $paletteId): ?array
    {
        $paletteId = \sanitize_key($paletteId);

        if ($paletteId === '') {
            return null;
        }

        $palettes = $this->getCustomPalettes();

        return $palettes[$paletteId] ?? null;
    }

    /**
     * Persist a custom palette.
     *
     * @param array<string, mixed> $palette
     */
    public function saveCustomPalette(array $palette): void
    {
        $palette = $this->sanitizePalette($palette);
        $paletteId = $palette['id'] ?? '';

        if ($paletteId === '') {
            return;
        }

        $palettes = $this->getCustomPalettes();
        $palettes[$paletteId] = $palette;

        \update_option(self::OPTION_CUSTOM_PALETTES, $palettes, false);
        $this->clearPaletteCache();
    }

    /**
     * Delete a custom palette if it exists.
     */
    public function deleteCustomPalette(string $paletteId): void
    {
        $paletteId = \sanitize_key($paletteId);
        if ($paletteId === '') {
            return;
        }

        $palettes = $this->getCustomPalettes();
        if (!isset($palettes[$paletteId])) {
            return;
        }

        unset($palettes[$paletteId]);
        \update_option(self::OPTION_CUSTOM_PALETTES, $palettes, false);
        $this->clearPaletteCache();
    }

    /**
     * Retrieve active theme settings.
     *
     * Automatically migrates legacy string format to new array format if detected.
     *
     * @return array{theme?: string, palette?: string}
     */
    public function getActiveThemeSettings(): array
    {
        $settings = \get_option(self::OPTION_ACTIVE_THEME, []);

        // Auto-migrate legacy string format to new array format
        if (!is_array($settings)) {
            $newSettings = [];

            // Migrate theme from legacy string format
            if (is_string($settings) && $settings !== '') {
                $newSettings['theme'] = \sanitize_key($settings);
            } else {
                $newSettings['theme'] = 'starter';
            }

            // Check for legacy palette option
            $legacyPalette = \get_option('fleekdash_active_palette', null);
            if (is_string($legacyPalette) && $legacyPalette !== '') {
                $newSettings['palette'] = \sanitize_key($legacyPalette);
            } else {
                $newSettings['palette'] = 'starter-fresh';
            }

            // Save migrated format and remove legacy option
            \update_option(self::OPTION_ACTIVE_THEME, $newSettings, false);
            \delete_option('fleekdash_active_palette');

            return [
                'theme' => $newSettings['theme'],
                'palette' => $newSettings['palette'],
            ];
        }

        return [
            'theme' => \sanitize_key($settings['theme'] ?? ''),
            'palette' => \sanitize_key($settings['palette'] ?? ''),
        ];
    }

    /**
     * Persist active theme settings.
     */
    public function setActiveThemeSettings(string $themeId, string $paletteId): void
    {
        $payload = [
            'theme' => \sanitize_key($themeId),
            'palette' => \sanitize_key($paletteId),
        ];

        \update_option(self::OPTION_ACTIVE_THEME, $payload, false);
    }

    /**
     * Determine if palette is referenced by any custom theme.
     */
    public function isPaletteInUse(string $paletteId): bool
    {
        $paletteId = \sanitize_key($paletteId);
        if ($paletteId === '') {
            return false;
        }

        $themes = $this->getCustomThemes();

        foreach ($themes as $theme) {
            $palettes = $theme['palettes'] ?? [];
            if (!is_array($palettes)) {
                continue;
            }

            if (in_array($paletteId, $palettes, true) || ($theme['default_palette'] ?? '') === $paletteId) {
                return true;
            }
        }

        return false;
    }

    /**
     * Clear cached themes.
     */
    public function clearThemeCache(): void
    {
        wp_cache_delete(self::CACHE_KEY_THEMES, self::CACHE_GROUP);
    }

    /**
     * Clear cached palettes.
     */
    public function clearPaletteCache(): void
    {
        wp_cache_delete(self::CACHE_KEY_PALETTES, self::CACHE_GROUP);
    }

    /**
     * Sanitize theme payload.
     *
     * @param array<string, mixed> $theme
     * @return array<string, mixed>
     */
    private function sanitizeTheme(array $theme): array
    {
        $themeId = \sanitize_key($theme['id'] ?? '');

        if ($themeId === '') {
            return [];
        }

        return [
            'id' => $themeId,
            'name' => \sanitize_text_field($theme['name'] ?? ''),
            'description' => sanitize_textarea_field($theme['description'] ?? ''),
            'author' => \sanitize_text_field($theme['author'] ?? ''),
            'version' => \sanitize_text_field($theme['version'] ?? '1.0.0'),
            'parent_theme' => \sanitize_key($theme['parent_theme'] ?? 'starter'),
            'default_palette' => \sanitize_key($theme['default_palette'] ?? ''),
            'palettes' => $this->sanitizePaletteIds($theme['palettes'] ?? []),
            'color_modes' => $this->sanitizeColorModes($theme['color_modes'] ?? []),
            'created_at' => \sanitize_text_field($theme['created_at'] ?? ''),
            'updated_at' => \sanitize_text_field($theme['updated_at'] ?? ''),
        ];
    }

    /**
     * Sanitize palette payload.
     *
     * @param array<string, mixed> $palette
     * @return array<string, mixed>
     */
    private function sanitizePalette(array $palette): array
    {
        $paletteId = \sanitize_key($palette['id'] ?? '');

        if ($paletteId === '') {
            return [];
        }

        $syncRaw = $palette['mode_sync_variables'] ?? $palette['modeSyncVariables'] ?? [];

        return [
            'id' => $paletteId,
            'name' => \sanitize_text_field($palette['name'] ?? ''),
            'description' => sanitize_textarea_field($palette['description'] ?? ''),
            'theme' => \sanitize_key($palette['theme'] ?? ''),
            'source' => \sanitize_key($palette['source'] ?? 'custom'),
            'wp_sync_key' => \sanitize_key($palette['wp_sync_key'] ?? $palette['wpSyncKey'] ?? ''),
            'mode_sync_variables' => $this->sanitizeModeSyncVariableNames($syncRaw),
            'modes' => $this->sanitizeColorModes($palette['modes'] ?? []),
            'created_at' => \sanitize_text_field($palette['created_at'] ?? ''),
            'updated_at' => \sanitize_text_field($palette['updated_at'] ?? ''),
        ];
    }

    /**
     * Sanitize list of CSS custom property names for cross-mode sync.
     *
     * @param mixed $list
     * @return array<int, string>
     */
    private function sanitizeModeSyncVariableNames($list): array
    {
        if (!\is_array($list)) {
            return [];
        }

        $out = [];

        foreach ($list as $item) {
            if (!\is_string($item)) {
                continue;
            }

            $varName = \preg_replace('/[^a-z0-9\-\_]/', '', \strtolower($item));
            if ($varName === '') {
                continue;
            }

            if (\strpos($varName, '--') !== 0) {
                $varName = '--' . \ltrim($varName, '-');
            }

            $out[] = $varName;
        }

        return \array_values(\array_unique($out));
    }

    /**
     * Sanitize palette IDs list.
     *
     * @param mixed $palettes
     * @return array<int, string>
     */
    private function sanitizePaletteIds($palettes): array
    {
        if (!is_array($palettes)) {
            return [];
        }

        $ids = [];

        foreach ($palettes as $paletteId) {
            $paletteId = \sanitize_key($paletteId);
            if ($paletteId !== '') {
                $ids[] = $paletteId;
            }
        }

        return array_values(array_unique($ids));
    }

    /**
     * Sanitize color modes payload.
     *
     * @param mixed $colorModes
     * @return array<string, array<string, string>>
     */
    private function sanitizeColorModes($colorModes): array
    {
        if (!is_array($colorModes)) {
            return [];
        }

        $sanitized = [];

        foreach ($colorModes as $mode => $variables) {
            $modeKey = \sanitize_key($mode);
            if ($modeKey === '' || !is_array($variables)) {
                continue;
            }

            $sanitized[$modeKey] = $this->sanitizeCssVariables($variables);
        }

        return $sanitized;
    }

    /**
     * Sanitize CSS variable map.
     *
     * @param mixed $variables
     * @return array<string, string>
     */
    private function sanitizeCssVariables($variables): array
    {
        if (!is_array($variables)) {
            return [];
        }

        $sanitized = [];

        foreach ($variables as $name => $value) {
            $varName = preg_replace('/[^a-z0-9\-\_]/', '', strtolower((string) $name));
            if ($varName === '') {
                continue;
            }

            // Ensure variables keep the --prefix
            if (strpos($varName, '--') !== 0) {
                $varName = '--' . ltrim($varName, '-');
            }

            $sanitized[$varName] = \sanitize_text_field((string) $value);
        }

        return $sanitized;
    }
}
