<?php

namespace App;

use App\FieldVisibility\FieldCatalogueRegistry;
use Illuminate\Support\Facades\DB;
use Mtc\MercuryDataModels\TenantHiddenField;

/**
 * Reads and persists the site-wide (per-tenant) set of hidden admin edit fields.
 *
 * Rows live on the central database (TenantHiddenField uses CentralConnection +
 * BelongsToTenant), so queries are automatically scoped to the current tenant.
 * A row existing means that field/tab is hidden.
 */
class TenantHiddenFieldRepository
{
    /**
     * Per-request cache of hidden keys keyed by entity.
     *
     * @var array<string, list<string>>
     */
    private array $cache = [];

    public function __construct(private readonly FieldCatalogueRegistry $registry)
    {
    }

    /**
     * Hidden field keys for an entity, scoped to the current tenant.
     *
     * @return list<string>
     */
    public function hiddenKeys(string $entity): array
    {
        if (! array_key_exists($entity, $this->cache)) {
            $this->cache[$entity] = TenantHiddenField::query()
                ->forEntity($entity)
                ->pluck('field_key')
                ->all();
        }

        return $this->cache[$entity];
    }

    /**
     * Persist the hidden set for an entity. Lockable keys are never stored, and the
     * stored set is reconciled to exactly match the requested (hideable) keys.
     *
     * @param list<string> $keys
     */
    public function sync(string $entity, array $keys): void
    {
        $catalogue = $this->registry->for($entity);

        $allowed = array_values(array_intersect(
            array_unique($keys),
            $catalogue->hideableKeys()
        ));

        $current = $this->hiddenKeys($entity);
        $toAdd = array_diff($allowed, $current);
        $toRemove = array_diff($current, $allowed);

        $connection = (new TenantHiddenField())->getConnectionName();

        DB::connection($connection)->transaction(function () use ($entity, $toAdd, $toRemove) {
            if ($toRemove !== []) {
                TenantHiddenField::query()
                    ->forEntity($entity)
                    ->whereIn('field_key', $toRemove)
                    ->delete();
            }

            foreach ($toAdd as $key) {
                TenantHiddenField::query()->create([
                    'entity' => $entity,
                    'field_key' => $key,
                ]);
            }
        });

        unset($this->cache[$entity]);
    }
}
