<?php

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedNamespaceFound -- APD is the established namespace shared with the Pro add-on and retained for backward compatibility.
namespace APD\Support;

defined('ABSPATH') || exit;

class AssetCatalog
{
    public const OPTION = 'apd_asset_catalog';

    public function register() : void
    {
        register_setting('apd_catalog_settings_group', self::OPTION, [
            'type' => 'array',
            'sanitize_callback' => [$this, 'sanitize'],
            'default' => $this->defaults(),
        ]);
    }

    public function defaults() : array
    {
        return RuntimeDataSchema::catalogDefaults();
    }

    public function all() : array
    {
        return wp_parse_args((array) get_option(self::OPTION, []), $this->defaults());
    }

    public function sanitize(mixed $input) : array
    {
        $input = is_array($input) ? $input : [];
        return RuntimeDataSchema::sanitizeCatalog($input, $this->all());
    }

    public function normalizeFilters(array|string|null $raw) : array
    {
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            $raw = is_array($decoded) ? $decoded : [];
        }

        $raw = is_array($raw) ? $raw : [];
        $types = [
            'designs',
            'fonts',
            'colors',
            'printingMethods',
            'shapes',
        ];

        $normalized = [];
        foreach ($types as $type) {
            $group = is_array($raw[$type] ?? null) ? $raw[$type] : [];
            $mode = sanitize_key((string) ($group['mode'] ?? 'all'));
            if (! in_array($mode, ['all', 'selected', 'none'], true)) {
                $mode = 'all';
            }
            $ids = [];
            foreach ((array) ($group['ids'] ?? []) as $id) {
                $id = sanitize_key((string) $id);
                if ($id !== '') {
                    $ids[] = $id;
                }
            }
            $normalized[$type] = [
                'mode' => $mode,
                'ids' => array_values(array_unique($ids)),
            ];
        }

        return $normalized;
    }


    public function getCollection(string $key) : array
    {
        $catalog = $this->all();
        return isset($catalog[$key]) && is_array($catalog[$key]) ? array_values($catalog[$key]) : [];
    }

    public function getItem(string $key, int $index) : ?array
    {
        $items = $this->getCollection($key);
        return $items[$index] ?? null;
    }

    public function saveItem(string $key, array $item, ?int $index = null) : int
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return 0;
        }

        $items = $this->getCollection($key);
        if ($index !== null && isset($items[$index])) {
            $items[$index] = $item;
            $savedIndex = $index;
        } else {
            $items[] = $item;
            $savedIndex = count($items) - 1;
        }

        $sanitized = $this->sanitize([$key => $items]);
        update_option(self::OPTION, $sanitized);

        return $savedIndex;
    }

    public function deleteItem(string $key, int $index) : void
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return;
        }

        $items = $this->getCollection($key);
        if (! isset($items[$index])) {
            return;
        }

        unset($items[$index]);
        $sanitized = $this->sanitize([$key => array_values($items)]);
        update_option(self::OPTION, $sanitized);
    }

    public function duplicateItem(string $key, int $index) : ?int
    {
        $item = $this->getItem($key, $index);
        if (! is_array($item)) {
            return null;
        }

        $labelBase = (string) ($item['label'] ?? $item['family'] ?? $item['id'] ?? 'item');
        $item['id'] = $this->buildUniqueId($key, (string) ($item['id'] ?? ''), $labelBase);
        if (isset($item['label']) && is_string($item['label'])) {
            $item['label'] = sprintf(/* translators: Placeholder values are populated from the current Asuka Designer record. */ __('%s copy', 'asuka-product-designer'), $item['label']);
        }

        return $this->saveItem($key, $item, null);
    }


    public function replaceCollection(string $key, array $items) : void
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return;
        }

        $sanitized = $this->sanitize([$key => array_values($items)]);
        update_option(self::OPTION, $sanitized);
    }

    public function appendItems(string $key, array $items) : int
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return 0;
        }

        $existing = $this->getCollection($key);
        $existing = array_merge($existing, array_values($items));
        $sanitized = $this->sanitize([$key => $existing]);
        update_option(self::OPTION, $sanitized);

        return count($sanitized[$key] ?? []);
    }


    public function nextUniqueId(string $key, string $preferredId, string $fallbackLabel) : string
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return $this->sanitizeId($preferredId, $fallbackLabel, rtrim($key, 's'));
        }

        $candidate = $this->sanitizeId($preferredId, $fallbackLabel, rtrim($key, 's'));
        $ids = array_map(static fn($item) => (string) ($item['id'] ?? ''), $this->getCollection($key));
        $base = $candidate;
        $suffix = 2;
        while (in_array($candidate, $ids, true)) {
            $candidate = $base . '-' . $suffix;
            $suffix += 1;
        }

        return $candidate;
    }

    public function moveItem(string $key, int $index, string $direction = 'up') : ?int
    {
        if (! in_array($key, RuntimeDataSchema::COLLECTION_KEYS, true)) {
            return null;
        }

        $items = $this->getCollection($key);
        if (! isset($items[$index])) {
            return null;
        }

        $target = $direction === 'down' ? $index + 1 : $index - 1;
        if (! isset($items[$target])) {
            return $index;
        }

        $current = $items[$index];
        $items[$index] = $items[$target];
        $items[$target] = $current;
        $this->replaceCollection($key, array_values($items));

        return $target;
    }

    public function getSelectedPricingRules(array|string|null $selection) : array
    {
        $selectedIds = $this->normalizeSelectedPricingRuleIds($selection);
        if ($selectedIds === []) {
            return [];
        }

        $allowed = array_flip($selectedIds);
        $items = [];
        foreach ($this->getCollection('pricing_rules') as $item) {
            if (! is_array($item)) {
                continue;
            }
            $id = sanitize_key((string) ($item['id'] ?? ''));
            if ($id === '' || ! isset($allowed[$id]) || (isset($item['enabled']) && ! $item['enabled'])) {
                continue;
            }
            $items[] = $item;
        }

        return array_values($items);
    }

    public function normalizeSelectedPricingRuleIds(array|string|null $selection) : array
    {
        if (is_string($selection)) {
            $decoded = json_decode($selection, true);
            $selection = is_array($decoded) ? $decoded : preg_split('/[\s,]+/', trim($selection));
        }

        $ids = [];
        foreach ((array) $selection as $id) {
            $id = sanitize_key((string) $id);
            if ($id !== '') {
                $ids[] = $id;
            }
        }

        return array_values(array_unique($ids));
    }

    public function buildPayload(array|string|null $filters = null) : array
    {
        $catalog = $this->all();
        $normalized = $this->normalizeFilters(is_array($filters) || is_string($filters) ? $filters : []);

        return [
            'schemaVersion' => RuntimeDataSchema::VERSION,
            'designs' => $this->applyFilterMode(array_map([$this, 'normalizeDesignRuntimeItem'], (array) ($catalog['designs'] ?? [])), $normalized['designs']),
            'fonts' => $this->applyFilterMode((array) ($catalog['fonts'] ?? []), $normalized['fonts']),
            'colors' => $this->applyFilterMode((array) ($catalog['colors'] ?? []), $normalized['colors']),
            'printingMethods' => $this->applyFilterMode((array) ($catalog['printing_methods'] ?? []), $normalized['printingMethods']),
            'shapes' => $this->applyFilterMode((array) ($catalog['shapes'] ?? []), $normalized['shapes']),
        ];
    }

    private function normalizeDesignRuntimeItem(array $item) : array
    {
        $label = sanitize_text_field((string) ($item['label'] ?? $item['name'] ?? ''));
        $url = esc_url_raw((string) ($item['url'] ?? $item['sourceUrl'] ?? $item['previewUrl'] ?? $item['thumbnail'] ?? ''));
        $svg = trim((string) ($item['svg'] ?? ''));
        $runtimeUrl = $url;
        if ($runtimeUrl === '' && $svg !== '') {
            $runtimeUrl = 'data:image/svg+xml;charset=utf-8,' . rawurlencode($svg);
        }

        $runtime = $item;
        if ($label !== '') {
            $runtime['label'] = $label;
            $runtime['name'] = $label;
        }
        if ($runtimeUrl !== '') {
            $runtime['url'] = $runtimeUrl;
            $runtime['sourceUrl'] = $runtimeUrl;
            $runtime['previewUrl'] = $runtimeUrl;
            $runtime['thumbnail'] = $runtimeUrl;
        }
        if (! isset($runtime['category'])) {
            $runtime['category'] = sanitize_text_field((string) ($item['category'] ?? ''));
        }
        $runtime['price'] = (float) str_replace(',', '.', (string) ($item['price'] ?? ($runtime['price'] ?? 0)));

        return $runtime;
    }

    private function normalizePricingRuleRuntimeItem(array $item) : array
    {
        $conditions = [];
        $rawConditions = trim((string) ($item['conditionsJson'] ?? '[]'));
        if ($rawConditions !== '') {
            $decoded = json_decode($rawConditions, true);
            if (is_array($decoded)) {
                $conditions = array_values(array_filter($decoded, 'is_array'));
            }
        }

        return [
            'id' => sanitize_key((string) ($item['id'] ?? '')),
            'label' => sanitize_text_field((string) ($item['label'] ?? $item['id'] ?? 'Rule')),
            'category' => sanitize_text_field((string) ($item['category'] ?? '')),
            'description' => sanitize_textarea_field((string) ($item['description'] ?? '')),
            'handler' => $this->normalizePricingHandler((string) ($item['handler'] ?? 'layersAmount')),
            'scope' => sanitize_key((string) ($item['scope'] ?? 'all')) ?: 'all',
            'match' => sanitize_key((string) ($item['match'] ?? 'all')) === 'any' ? 'any' : 'all',
            'enabled' => ! isset($item['enabled']) || ! empty($item['enabled']),
            'target' => array_filter([
                'types' => $this->normalizePricingTargetTypes((string) ($item['targetTypes'] ?? '')),
                'view' => $this->sanitizeCsv((string) ($item['targetViews'] ?? '')),
            ], static fn($value) => is_array($value) ? $value !== [] : $value !== null),
            'conditions' => $conditions,
        ];
    }

    private function applyFilterMode(array $items, array $filter) : array
    {
        $items = array_values(array_filter(array_map([$this, 'ensureItemEnabled'], $items), static fn($item) => is_array($item) && ! empty($item['enabled'])));
        $mode = $filter['mode'] ?? 'all';

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

        if ($mode === 'selected') {
            $allowed = array_flip((array) ($filter['ids'] ?? []));
            if (empty($allowed)) {
                return [];
            }
            $items = array_values(array_filter($items, static function ($item) use ($allowed) {
                return isset($allowed[(string) ($item['id'] ?? '')]);
            }));
        }

        return array_values($items);
    }

    private function ensureItemEnabled(array $item) : array
    {
        $item['enabled'] = ! isset($item['enabled']) || (bool) $item['enabled'];
        return $item;
    }


    private function buildUniqueId(string $key, string $currentId, string $fallbackLabel) : string
    {
        return $this->nextUniqueId($key, $currentId !== '' ? $currentId . '-copy' : '', $fallbackLabel . ' copy');
    }

    private function sanitizeDesigns(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            $url = esc_url_raw((string) ($row['url'] ?? $row['sourceUrl'] ?? $row['previewUrl'] ?? ''));
            $svg = trim((string) ($row['svg'] ?? ''));
            if ($label === '' && $url === '' && $svg === '') {
                continue;
            }

            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label, 'design'),
                'label' => $label !== '' ? $label : __('Untitled design', 'asuka-product-designer'),
                'category' => sanitize_text_field((string) ($row['category'] ?? '')),
                'description' => sanitize_textarea_field((string) ($row['description'] ?? '')),
                'url' => $url,
                'svg' => $svg,
                'price' => $this->sanitizeFloatString($row['price'] ?? 0),
                'enabled' => ! empty($row['enabled']),
            ];
        }
        return $items;
    }

    private function sanitizeFonts(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            $family = sanitize_text_field((string) ($row['family'] ?? ''));
            if ($label === '' && $family === '') {
                continue;
            }

            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label ?: $family, 'font'),
                'label' => $label !== '' ? $label : $family,
                'family' => $family !== '' ? $family : $label,
                'category' => sanitize_text_field((string) ($row['category'] ?? '')),
                'source' => sanitize_key((string) ($row['source'] ?? 'custom')) ?: 'custom',
                'cssUrl' => esc_url_raw((string) ($row['cssUrl'] ?? '')),
                'fileUrl' => esc_url_raw((string) ($row['fileUrl'] ?? '')),
                'weights' => $this->sanitizeCsv((string) ($row['weights'] ?? '')),
                'enabled' => ! empty($row['enabled']),
            ];
        }
        return $items;
    }

    private function sanitizeColors(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            $value = sanitize_text_field((string) ($row['value'] ?? ''));
            if ($label === '' && $value === '') {
                continue;
            }

            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label ?: $value, 'color'),
                'label' => $label !== '' ? $label : $value,
                'value' => $value,
                'group' => sanitize_text_field((string) ($row['group'] ?? '')),
                'price' => $this->sanitizeFloatString($row['price'] ?? 0),
                'enabled' => ! empty($row['enabled']),
            ];
        }
        return $items;
    }

    private function sanitizePrintingMethods(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            if ($label === '') {
                continue;
            }
            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label, 'print'),
                'label' => $label,
                'category' => sanitize_text_field((string) ($row['category'] ?? '')),
                'description' => sanitize_textarea_field((string) ($row['description'] ?? '')),
                'price' => $this->sanitizeFloatString($row['price'] ?? 0),
                'colors' => $this->sanitizeCsv((string) ($row['colors'] ?? '')),
                'enabled' => ! empty($row['enabled']),
            ];
        }
        return $items;
    }

    private function sanitizePricingRules(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }

            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            $handler = sanitize_key((string) ($row['handler'] ?? ''));
            $conditionsJson = trim((string) ($row['conditionsJson'] ?? ''));
            if ($label === '' && $handler === '' && $conditionsJson === '') {
                continue;
            }

            $builderMode = sanitize_key((string) ($row['builderMode'] ?? 'threshold_fixed'));
            if (! in_array($builderMode, ['threshold_fixed', 'threshold_percent', 'step_fixed', 'step_percent', 'raw_json'], true)) {
                $builderMode = 'threshold_fixed';
            }
            $builderOperator = sanitize_key((string) ($row['builderOperator'] ?? '>='));
            if (! in_array($builderOperator, ['>', '<', '>=', '<=', '=='], true)) {
                $builderOperator = '>=';
            }
            $builderAxis = sanitize_key((string) ($row['builderAxis'] ?? 'value'));
            if (! in_array($builderAxis, ['value', 'width', 'height'], true)) {
                $builderAxis = 'value';
            }
            $builderValue = trim((string) ($row['builderValue'] ?? ''));
            $builderPrice = trim((string) ($row['builderPrice'] ?? ''));

            if ($builderMode !== 'raw_json') {
                $generated = [];
                if ($builderValue !== '') {
                    $condition = [
                        'operator' => str_starts_with($builderMode, 'step_') ? '%' : $builderOperator,
                        'price' => str_ends_with($builderMode, '_percent') && $builderPrice !== '' && ! str_contains($builderPrice, '%') ? $builderPrice . '%' : $builderPrice,
                    ];
                    if ($builderAxis === 'width') {
                        $condition['width'] = (float) str_replace(',', '.', $builderValue);
                    } elseif ($builderAxis === 'height') {
                        $condition['height'] = (float) str_replace(',', '.', $builderValue);
                    } else {
                        $condition['value'] = (float) str_replace(',', '.', $builderValue);
                    }
                    $generated[] = $condition;
                }
                $conditionsJson = wp_json_encode($generated, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
            }

            $decodedConditions = json_decode($conditionsJson !== '' ? $conditionsJson : '[]', true);
            $conditions = is_array($decodedConditions) ? array_values(array_filter($decodedConditions, 'is_array')) : [];
            if ($conditions !== []) {
                $firstCondition = $conditions[0];
                if (is_array($firstCondition)) {
                    $inferredAxis = isset($firstCondition['width']) ? 'width' : (isset($firstCondition['height']) ? 'height' : 'value');
                    $inferredValue = $firstCondition[$inferredAxis] ?? ($firstCondition['value'] ?? '');
                    $inferredPrice = (string) ($firstCondition['price'] ?? '');
                    $isPercentPrice = is_string($inferredPrice) && str_contains($inferredPrice, '%');
                    $inferredMode = ($firstCondition['operator'] ?? '') === '%' ? ($isPercentPrice ? 'step_percent' : 'step_fixed') : ($isPercentPrice ? 'threshold_percent' : 'threshold_fixed');
                    $builderMode = $builderMode === 'raw_json' ? 'raw_json' : $inferredMode;
                    $builderOperator = (($firstCondition['operator'] ?? '') === '%' ? $builderOperator : sanitize_key((string) ($firstCondition['operator'] ?? $builderOperator))) ?: $builderOperator;
                    $builderAxis = $inferredAxis;
                    $builderValue = (string) $inferredValue;
                    $builderPrice = $inferredPrice;
                }
            }

            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label, 'pricing-rule'),
                'label' => $label !== '' ? $label : __('Untitled pricing rule', 'asuka-product-designer'),
                'category' => sanitize_text_field((string) ($row['category'] ?? '')),
                'description' => sanitize_textarea_field((string) ($row['description'] ?? '')),
                'handler' => $handler !== '' ? $handler : 'layersAmount',
                'scope' => sanitize_key((string) ($row['scope'] ?? 'all')) ?: 'all',
                'match' => sanitize_key((string) ($row['match'] ?? 'all')) === 'any' ? 'any' : 'all',
                'targetTypes' => implode(', ', $this->sanitizeCsv((string) ($row['targetTypes'] ?? ''))),
                'targetViews' => implode(', ', $this->sanitizeCsv((string) ($row['targetViews'] ?? ''))),
                'builderMode' => $builderMode,
                'builderOperator' => $builderOperator,
                'builderAxis' => $builderAxis,
                'builderValue' => sanitize_text_field($builderValue),
                'builderPrice' => sanitize_text_field($builderPrice),
                'conditionsJson' => wp_json_encode($conditions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
                'enabled' => ! empty($row['enabled']),
            ];
        }

        return $items;
    }

    private function sanitizeShapes(array $rows) : array
    {
        $items = [];
        foreach ($rows as $row) {
            if (! is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            $svgPath = trim((string) ($row['svgPath'] ?? ''));
            $sourceUrl = esc_url_raw((string) ($row['sourceUrl'] ?? ''));
            if ($label === '' && $svgPath === '' && $sourceUrl === '') {
                continue;
            }
            $items[] = [
                'id' => $this->sanitizeId((string) ($row['id'] ?? ''), $label, 'shape'),
                'label' => $label !== '' ? $label : __('Untitled shape', 'asuka-product-designer'),
                'category' => sanitize_text_field((string) ($row['category'] ?? '')),
                'type' => sanitize_key((string) ($row['type'] ?? 'svg')) ?: 'svg',
                'svgPath' => $svgPath,
                'viewBox' => sanitize_text_field((string) ($row['viewBox'] ?? '0 0 100 100')),
                'fill' => sanitize_text_field((string) ($row['fill'] ?? '')),
                'stroke' => sanitize_text_field((string) ($row['stroke'] ?? '')),
                'price' => $this->sanitizeFloatString($row['price'] ?? 0),
                'sourceUrl' => $sourceUrl,
                'enabled' => ! empty($row['enabled']),
            ];
        }
        return $items;
    }

    private function sanitizeId(string $id, string $fallbackLabel, string $prefix) : string
    {
        $id = sanitize_key($id);
        if ($id !== '') {
            return $id;
        }

        $fallback = sanitize_title($fallbackLabel);
        if ($fallback === '') {
            $fallback = $prefix . '-' . wp_generate_password(6, false, false);
        }

        return $fallback;
    }

    private function sanitizeFloatString(mixed $value) : string
    {
        return (string) (float) str_replace(',', '.', (string) $value);
    }

    private function sanitizeCsv(string $value) : array
    {
        $parts = preg_split('/[\s,]+/', trim($value));
        if (! is_array($parts)) {
            return [];
        }
        $items = [];
        foreach ($parts as $part) {
            $part = sanitize_key((string) $part);
            if ($part !== '') {
                $items[] = $part;
            }
        }
        return array_values(array_unique($items));
    }

    private function normalizePricingHandler(string $handler) : string
    {
        $normalized = sanitize_key($handler);
        return match ($normalized) {
            'layersamount' => 'layersAmount',
            'textlength' => 'textLength',
            'textlines' => 'textLines',
            'usedcolorsamount' => 'usedColorsAmount',
            'scale' => 'scale',
            'layersize' => 'layerSize',
            'canvassize' => 'canvasSize',
            default => $handler !== '' ? (string) $handler : 'layersAmount',
        };
    }

    private function normalizePricingTargetTypes(string $value) : array
    {
        $parts = preg_split('/[\s,]+/', trim($value));
        if (! is_array($parts)) {
            return [];
        }

        $items = [];
        foreach ($parts as $part) {
            $normalized = sanitize_key((string) $part);
            if ($normalized === '') {
                continue;
            }
            $items[] = match ($normalized) {
                'text', 'textbox', 'itext', 'i-text' => 'Text',
                'image', 'images' => 'Image',
                'design', 'designs', 'group' => 'Design',
                'tool', 'tools' => 'Tools',
                default => (string) $part,
            };
        }

        return array_values(array_unique($items));
    }
}
