<?php
/**
 * Global variable and color palette writes for Bricks options.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Gateways;

use LolaCoreForBricks\Support\BricksMetaKeys;

/**
 * Writes bricks_global_variables and bricks_color_palette.
 */
class VariableWriteGateway
{
    public function __construct(
        private readonly ?VariableGateway $readGateway = new VariableGateway()
    ) {
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function setGlobalVariables(array $params): array
    {
        $items = $this->normalizeVariableInput($params);
        if ($items === []) {
            return $this->failure(
                'Tell me which variables to create or update — a single name or a batch works.',
                'missing_variables'
            );
        }

        $variables = $this->loadRawVariables();
        $created   = [];
        $updated   = [];

        foreach ($items as $item) {
            $name = trim((string) ($item['name'] ?? ''));
            if ($name === '') {
                continue;
            }

            $entry = [
                'name'     => $name,
                'value'    => (string) ($item['value'] ?? ''),
                'type'     => (string) ($item['type'] ?? $this->inferType($item['value'] ?? '')),
                'category' => (string) ($item['category'] ?? ''),
            ];

            $index = $this->findVariableIndexByName($variables, $name);
            if ($index !== null) {
                $variables[$index] = array_merge($variables[$index], $entry);
                $updated[]         = $this->summarizeVariable($variables[$index]);
                continue;
            }

            $variables[] = $entry;
            $created[]   = $this->summarizeVariable($entry);
        }

        if ($created === [] && $updated === []) {
            return $this->failure('I could not find any valid variable names in that batch.', 'invalid_variables');
        }

        update_option(BricksMetaKeys::OPTION_GLOBAL_VARIABLES, array_values($variables));

        return [
            'success'  => true,
            'message'  => 'Global variables saved.',
            'created'  => $created,
            'updated'  => $updated,
            'count'    => count($created) + count($updated),
        ];
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function deleteGlobalVariable(array $params): array
    {
        $ref = trim((string) ($params['variable'] ?? $params['name'] ?? ''));
        if ($ref === '') {
            return $this->failure('Tell me which variable to remove.', 'missing_variable');
        }

        $variables = $this->loadRawVariables();
        $index     = $this->findVariableIndexByName($variables, ltrim($ref, '-'));
        if ($index === null) {
            return $this->failure('I could not find that global variable.', 'variable_not_found');
        }

        $removed = $variables[$index];
        unset($variables[$index]);
        update_option(BricksMetaKeys::OPTION_GLOBAL_VARIABLES, array_values($variables));

        return [
            'success'  => true,
            'message'  => 'Global variable removed.',
            'variable' => $this->summarizeVariable($removed),
        ];
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function updateColorPalette(array $params): array
    {
        $palettesInput = $this->normalizePaletteInput($params);
        if ($palettesInput === []) {
            return $this->failure('Tell me which color palette to create or update.', 'missing_palette');
        }

        $palettes = get_option('bricks_color_palette', []);
        if (!is_array($palettes)) {
            $palettes = [];
        }

        $created = [];
        $updated = [];

        foreach ($palettesInput as $input) {
            $paletteId = (string) ($input['id'] ?? '');
            $index     = $paletteId !== '' ? $this->findPaletteIndexById($palettes, $paletteId) : null;

            if ($index === null && $paletteId === '' && !empty($input['name'])) {
                $index = $this->findPaletteIndexByName($palettes, (string) $input['name']);
            }

            $colors = $this->normalizePaletteColors($input['colors'] ?? []);

            if ($index !== null) {
                $existing = $palettes[$index];
                if (!empty($input['name'])) {
                    $existing['name'] = (string) $input['name'];
                }
                if ($colors !== []) {
                    $existing['colors'] = $colors;
                }
                $palettes[$index] = $existing;
                $updated[]        = $this->summarizePalette($existing);
                continue;
            }

            $palette = [
                'id'     => $paletteId !== '' ? $paletteId : $this->generateId($palettes),
                'name'   => (string) ($input['name'] ?? 'Palette'),
                'colors' => $colors,
            ];
            $palettes[] = $palette;
            $created[]  = $this->summarizePalette($palette);
        }

        update_option('bricks_color_palette', array_values($palettes));

        return [
            'success'  => true,
            'message'  => 'Color palette saved.',
            'created'  => $created,
            'updated'  => $updated,
            'count'    => count($created) + count($updated),
        ];
    }

    /**
     * @param array<string, mixed> $params
     * @return array<int, array<string, mixed>>
     */
    private function normalizeVariableInput(array $params): array
    {
        if (isset($params['variables']) && is_array($params['variables'])) {
            return array_values(array_filter($params['variables'], 'is_array'));
        }

        $name = trim((string) ($params['name'] ?? ''));
        if ($name === '') {
            return [];
        }

        return [[
            'name'     => $name,
            'value'    => $params['value'] ?? '',
            'type'     => $params['type'] ?? null,
            'category' => $params['category'] ?? '',
        ]];
    }

    /**
     * @param array<string, mixed> $params
     * @return array<int, array<string, mixed>>
     */
    private function normalizePaletteInput(array $params): array
    {
        if (isset($params['palettes']) && is_array($params['palettes'])) {
            return array_values(array_filter($params['palettes'], 'is_array'));
        }

        if (isset($params['palette']) && is_array($params['palette'])) {
            return [$params['palette']];
        }

        if (isset($params['name']) || isset($params['colors'])) {
            return [[
                'id'     => $params['id'] ?? '',
                'name'   => $params['name'] ?? 'Palette',
                'colors' => $params['colors'] ?? [],
            ]];
        }

        return [];
    }

    /**
     * @param mixed $colors
     * @return array<int, array<string, mixed>>
     */
    private function normalizePaletteColors(mixed $colors): array
    {
        if (!is_array($colors)) {
            return [];
        }

        $normalized = [];
        foreach ($colors as $color) {
            if (!is_array($color)) {
                continue;
            }

            $entry = $color;
            if (empty($entry['id'])) {
                $entry['id'] = $this->generateId($normalized);
            }

            if (!empty($color['hex']) && empty($entry['light'])) {
                $entry['light'] = (string) $color['hex'];
            }

            if (!empty($color['name']) && empty($entry['raw'])) {
                $entry['raw'] = 'var(--bricks-color-' . $entry['id'] . ')';
            }

            $normalized[] = $entry;
        }

        return $normalized;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function loadRawVariables(): array
    {
        $raw = get_option(BricksMetaKeys::OPTION_GLOBAL_VARIABLES, []);
        if (!is_array($raw)) {
            return [];
        }

        $variables = [];
        foreach ($raw as $variable) {
            if (is_array($variable) && !empty($variable['name'])) {
                $variables[] = $variable;
            }
        }

        return $variables;
    }

    /**
     * @param array<int, array<string, mixed>> $variables
     */
    private function findVariableIndexByName(array $variables, string $name): ?int
    {
        foreach ($variables as $index => $variable) {
            if (strcasecmp((string) ($variable['name'] ?? ''), $name) === 0) {
                return $index;
            }
        }

        return null;
    }

    /**
     * @param array<int, array<string, mixed>> $palettes
     */
    private function findPaletteIndexById(array $palettes, string $paletteId): ?int
    {
        foreach ($palettes as $index => $palette) {
            if ((string) ($palette['id'] ?? '') === $paletteId) {
                return $index;
            }
        }

        return null;
    }

    /**
     * @param array<int, array<string, mixed>> $palettes
     */
    private function findPaletteIndexByName(array $palettes, string $name): ?int
    {
        foreach ($palettes as $index => $palette) {
            if (strcasecmp((string) ($palette['name'] ?? ''), $name) === 0) {
                return $index;
            }
        }

        return null;
    }

    /**
     * @param array<string, mixed> $variable
     * @return array<string, mixed>
     */
    private function summarizeVariable(array $variable): array
    {
        return [
            'name'     => (string) ($variable['name'] ?? ''),
            'value'    => (string) ($variable['value'] ?? ''),
            'type'     => (string) ($variable['type'] ?? 'custom'),
            'category' => (string) ($variable['category'] ?? ''),
        ];
    }

    /**
     * @param array<string, mixed> $palette
     * @return array<string, mixed>
     */
    private function summarizePalette(array $palette): array
    {
        $colors = is_array($palette['colors'] ?? null) ? $palette['colors'] : [];

        return [
            'id'           => (string) ($palette['id'] ?? ''),
            'name'         => (string) ($palette['name'] ?? ''),
            'colors_count' => count($colors),
        ];
    }

    private function inferType(mixed $value): string
    {
        if (!is_string($value)) {
            return 'custom';
        }

        if (preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $value) === 1) {
            return 'color';
        }

        if (str_starts_with($value, 'var(--')) {
            return 'color';
        }

        return 'custom';
    }

    /**
     * @param array<int, array<string, mixed>> $existing
     */
    private function generateId(array $existing): string
    {
        $used = [];
        foreach ($existing as $item) {
            if (!empty($item['id'])) {
                $used[(string) $item['id']] = true;
            }
        }

        do {
            $id = class_exists(\Bricks\Helpers::class)
                ? (string) \Bricks\Helpers::generate_random_id(false)
                : substr(md5(uniqid('', true)), 0, 6);
        } while (isset($used[$id]));

        return $id;
    }

    /**
     * @return array{success: false, message: string, error: string}
     */
    private function failure(string $message, string $error): array
    {
        return [
            'success' => false,
            'message' => $message,
            'error'   => $error,
        ];
    }
}
