<?php
/**
 * Theme style writes for Bricks options.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Gateways;

use LolaCoreForBricks\Support\BricksCodeGuard;
use LolaCoreForBricks\Support\BricksMetaKeys;
use LolaCoreForBricks\Support\CssTextGuard;
use LolaCoreForBricks\Support\SettingsKeyGrammar;

/**
 * Writes bricks_theme_styles including conditions.
 */
class ThemeStyleWriteGateway
{
    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function updateThemeStyle(array $params): array
    {
        $ref = trim((string) ($params['style'] ?? $params['style_id'] ?? ''));
        $styles = get_option(BricksMetaKeys::OPTION_THEME_STYLES, []);
        if (!is_array($styles)) {
            $styles = [];
        }

        $styleId = $ref !== '' ? $this->resolveStyleId($styles, $ref) : null;
        $isNew   = $styleId === null;

        if ($isNew) {
            $label = trim((string) ($params['label'] ?? $params['name'] ?? ''));
            if ($label === '') {
                return $this->failure(
                    'Tell me which theme style to update, or provide a label to create one.',
                    'missing_style'
                );
            }

            $styleId = $this->generateStyleId($styles);
            $styles[$styleId] = [
                'label'    => $label,
                'settings' => [],
            ];
        }

        $style = is_array($styles[$styleId] ?? null) ? $styles[$styleId] : ['label' => '', 'settings' => []];

        if (isset($params['label']) && trim((string) $params['label']) !== '') {
            $style['label'] = trim((string) $params['label']);
        }

        $settings = is_array($style['settings'] ?? null) ? $style['settings'] : [];

        if (isset($params['settings']) && is_array($params['settings'])) {
            if ($settings === [] && $params['settings'] === []) {
                return $this->failure('I need at least one setting to update this theme style.', 'missing_settings');
            }

            if ($rejection = BricksCodeGuard::validateSettings($params['settings'])) {
                return $rejection;
            }

            if ($rejection = CssTextGuard::validateSettings($params['settings'])) {
                return $rejection;
            }

            $settings = array_merge($settings, $this->normalizeIncomingSettings($params['settings']));
        }

        if (isset($params['conditions']) && is_array($params['conditions'])) {
            $existingConditions = is_array($settings['conditions']['conditions'] ?? null)
                ? $settings['conditions']['conditions']
                : [];
            $settings['conditions'] = [
                'conditions' => $this->mergeConditions($existingConditions, $params['conditions']),
            ];
        }

        $style['settings'] = $settings;
        $styles[$styleId]  = $style;
        update_option(BricksMetaKeys::OPTION_THEME_STYLES, $styles);

        return [
            'success'    => true,
            'message'    => $isNew ? 'Theme style created.' : 'Theme style updated.',
            'style_id'   => $styleId,
            'style'      => $this->summarizeStyle($styleId, $style),
            'created'    => $isNew,
        ];
    }

    /**
     * @param array<string, array<string, mixed>> $styles
     */
    private function resolveStyleId(array $styles, string $ref): ?string
    {
        if (isset($styles[$ref])) {
            return $ref;
        }

        foreach ($styles as $styleId => $style) {
            if (!is_array($style)) {
                continue;
            }

            if (strcasecmp((string) ($style['label'] ?? ''), $ref) === 0) {
                return (string) $styleId;
            }

            if ((string) ($style['id'] ?? '') === $ref) {
                return (string) $styleId;
            }
        }

        return null;
    }

    /**
     * @param array<int, mixed> $existing
     * @param array<int, mixed> $incoming
     * @return array<int, mixed>
     */
    private function mergeConditions(array $existing, array $incoming): array
    {
        if ($incoming === []) {
            return $existing;
        }

        if ($existing === []) {
            return array_values($incoming);
        }

        return array_values(array_merge($existing, $incoming));
    }

    /**
     * @param array<string, mixed> $settings
     * @return array<string, mixed>
     */
    private function normalizeIncomingSettings(array $settings): array
    {
        $normalized = [];

        foreach ($settings as $key => $value) {
            if (!is_string($key)) {
                continue;
            }

            $parsed = SettingsKeyGrammar::parse($key);
            if ($parsed['legacy']) {
                $key = SettingsKeyGrammar::compose(
                    $parsed['base'],
                    $parsed['breakpoint'],
                    $parsed['state'],
                    $parsed['variant']
                );
            }

            $normalized[$key] = $value;
        }

        return $normalized;
    }

    /**
     * @param array<string, mixed> $style
     * @return array<string, mixed>
     */
    private function summarizeStyle(string $styleId, array $style): array
    {
        $settings   = is_array($style['settings'] ?? null) ? $style['settings'] : [];
        $conditions = is_array($settings['conditions']['conditions'] ?? null)
            ? $settings['conditions']['conditions']
            : [];

        return [
            'id'                 => $styleId,
            'name'               => (string) ($style['label'] ?? $styleId),
            'settings_count'     => count($settings),
            'conditions_count'   => count($conditions),
        ];
    }

    /**
     * @param array<string, array<string, mixed>> $styles
     */
    private function generateStyleId(array $styles): string
    {
        do {
            $id = class_exists(\Bricks\Helpers::class)
                ? (string) \Bricks\Helpers::generate_random_id(false)
                : substr(md5(uniqid('', true)), 0, 6);
        } while (isset($styles[$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,
        ];
    }
}
