<?php

declare(strict_types=1);

namespace CliqThemes\StoreLocator\Data\Definition;

defined('ABSPATH') || exit;

use CliqThemes\ORM\Form\FieldStorageDescriptor;
use CliqThemes\ORM\Form\Storage\JsonColumnStrategy;
use CliqThemes\ORM\Form\Storage\PassthroughStrategy;
use CliqThemes\ORM\Form\Storage\PivotStorageStrategy;
use CliqThemes\ORM\Form\Storage\TableStorageStrategy;
use CliqThemes\ORM\Form\StorageStrategyRegistry;
use CliqThemes\ORM\Model;
use CliqThemes\StoreLocator\Data\FormSelectOptions;
use CliqThemes\StoreLocator\Models\Store;
use CliqThemes\StoreLocator\Storage\LocationStorageStrategy;
use CliqThemes\StoreLocator\Storage\MetaTableStorageStrategy;

/**
 * Shared services and lookups passed to every {@see ProviderDefinition}. Option
 * lists and the store tab map are memoised so they cost the same single query /
 * build they did when computed once in the old orchestrator.
 */
final class ProviderContext
{
    /** @var array<int, array<string, mixed>>|null */
    private ?array $brandOptions = null;

    /** @var array<int, array<string, mixed>>|null */
    private ?array $categoryOptions = null;

    /** @var array<string, mixed>|null */
    private ?array $storeTabSections = null;

    public function __construct(private FormSelectOptions $options)
    {
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function brandOptions(): array
    {
        return $this->brandOptions ??= $this->options->brands();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function categoryOptions(): array
    {
        return $this->categoryOptions ??= $this->options->categories();
    }

    /**
     * The store form's tabs and their section (group) ids/labels. Drives the
     * custom-field placement picker and the store form's field injection.
     *
     * @return array<string, array{label: string, groups: array<string, string>}>
     */
    public function storeTabSections(): array
    {
        return $this->storeTabSections ??= StoreProviderDefinition::tabSectionMap();
    }

    /**
     * The default meta_table storage descriptor for a store meta key.
     *
     * @param array<string, mixed> $overrides
     *
     * @return array<string, mixed>
     */
    public function storagePreset(string $table, string $metaKey, array $overrides = []): array
    {
        return array_merge([
            'target' => 'meta_table',
            'table' => $table,
            'column' => 'meta_value',
            'store_key_column' => 'store_id',
            'meta_key_column' => 'meta_key',
            'key' => $metaKey,
        ], $overrides);
    }

    /**
     * Storage strategy registry shared by the schema-aware coordinators. Pass a
     * base model so the passthrough strategy targets the right table.
     */
    public function strategyRegistry(?Model $baseModel = null): StorageStrategyRegistry
    {
        $base = $baseModel ?? new Store();
        $registry = new StorageStrategyRegistry();
        $registry->register('base', static fn () => new PassthroughStrategy($base));
        $registry->register('table', static fn (FieldStorageDescriptor $descriptor, array $context = []): TableStorageStrategy => new TableStorageStrategy($descriptor->options));
        $registry->register('pivot', static fn (FieldStorageDescriptor $descriptor, array $context = []): PivotStorageStrategy => new PivotStorageStrategy($descriptor->options));
        $registry->register('json', static fn (FieldStorageDescriptor $descriptor, array $context = []): JsonColumnStrategy => new JsonColumnStrategy($descriptor->options));
        $registry->register('meta_table', static fn (FieldStorageDescriptor $descriptor, array $context = []): MetaTableStorageStrategy => new MetaTableStorageStrategy($descriptor->options));
        $registry->register('location', static fn (FieldStorageDescriptor $descriptor, array $context = []): LocationStorageStrategy => new LocationStorageStrategy($descriptor->options));

        return $registry;
    }
}
