<?php

declare(strict_types=1);

require_once __DIR__ . '/StyleDefinitionRuntime.php';

use Activetwist\Jsonm\JsonmProfile;
use Activetwist\Jsonm\Targets\MyronCssTarget;
use Activetwist\Jsonm\Validator;

final class DesignSystemRegistry
{
    private string $baselinePath;
    private string $archetypesDir;
    private string $definitionsDir;
    private string $manifestPath;
    private string $currentDefinitionPath;
    private string $runtimeTokensPath;
    private string $runtimeFontsPath;
    private string $tokenMapPath;

    public function __construct(?array $paths = null)
    {
        $root = StyleDefinitionRuntime::projectRoot();
        $this->baselinePath = (string) ($paths['baselinePath'] ?? ($root . '/app/jsonm/baselines/apollo-v0.1.0.jsonm'));
        $this->archetypesDir = (string) ($paths['archetypesDir'] ?? ($root . '/app/jsonm/archetypes'));
        $this->definitionsDir = (string) ($paths['definitionsDir'] ?? ($root . '/app/jsonm/definitions'));
        $this->manifestPath = (string) ($paths['manifestPath'] ?? ($this->definitionsDir . '/index.json'));
        $this->currentDefinitionPath = (string) ($paths['currentDefinitionPath'] ?? ($root . '/app/jsonm/current-definition.json'));
        $this->runtimeTokensPath = (string) ($paths['runtimeTokensPath'] ?? ($root . '/app/assets/runtime/css/runtime-token-overrides.css'));
        $this->runtimeFontsPath = (string) ($paths['runtimeFontsPath'] ?? ($root . '/app/assets/runtime/css/runtime-fonts.css'));
        $this->tokenMapPath = (string) ($paths['tokenMapPath'] ?? ($root . '/app/jsonm/maps/token-map.json'));
    }

    private function myronCmsProfile(): JsonmProfile
    {
        return new JsonmProfile(
            allowedCorpusIds: ['activetwist-design-corpus/v1'],
            cssVarPrefix: 'myron',
            baselinePath: $this->baselinePath,
            tokenMapPath: $this->tokenMapPath,
            archetypesDir: $this->archetypesDir,
            runtimeTokensPath: $this->runtimeTokensPath,
            runtimeFontsPath: $this->runtimeFontsPath,
        );
    }

    public function ensureStorage(): void
    {
        if (!is_dir($this->definitionsDir)) {
            mkdir($this->definitionsDir, 0777, true);
        }

        if (!is_file($this->manifestPath)) {
            StyleDefinitionRuntime::writeFile(
                $this->manifestPath,
                StyleDefinitionRuntime::jsonEncodePretty([
                    'definitions' => [],
                    'disabledPackagedArchetypeIds' => [],
                ])
            );
        }

        if (!is_file($this->currentDefinitionPath)) {
            $baseline = $this->baselineRecord();
            $baseline['appliedAt'] = null;
            StyleDefinitionRuntime::writeFile(
                $this->currentDefinitionPath,
                StyleDefinitionRuntime::jsonEncodePretty($this->portableRecord($baseline))
            );
        }
    }

    public function currentDefinition(): array
    {
        $this->ensureStorage();
        $current = StyleDefinitionRuntime::loadJson($this->currentDefinitionPath);
        $record = $this->hydrateRecord($current);
        if ((string) ($record['source'] ?? '') === 'baseline') {
            $baseline = $this->baselineRecord();
            $baseline['appliedAt'] = $record['appliedAt'] ?? null;
            return $baseline;
        }
        if ((string) ($record['source'] ?? '') !== 'baseline' && !is_file((string) ($record['path'] ?? ''))) {
            return $this->applyBaseline();
        }

        return $record;
    }

    public function baselineRecord(): array
    {
        $definition = StyleDefinitionRuntime::loadJson($this->baselinePath);
        $createdAt = $this->fileTimestamp($this->baselinePath);

        return [
            'id' => (string) ($definition['id'] ?? 'apollo-v0.1.0-baseline'),
            'name' => (string) ($definition['name'] ?? 'MyronCMS Default'),
            'kind' => (string) ($definition['kind'] ?? 'baseline'),
            'description' => (string) ($definition['description'] ?? 'The default design system for MyronCMS.'),
            'source' => 'baseline',
            'filename' => basename($this->baselinePath),
            'path' => $this->baselinePath,
            'createdAt' => $createdAt,
            'updatedAt' => $createdAt,
            'appliedAt' => null,
        ];
    }

    public function listDefinitions(): array
    {
        $this->ensureStorage();
        $definitions = $this->loadManifest()['definitions'];

        return array_map(fn (array $entry): array => $this->hydrateRecord($entry), $definitions);
    }

    public function listSelectableDefinitions(): array
    {
        $this->ensureStorage();

        $records = array_merge(
            [$this->baselineRecord()],
            $this->listPackagedArchetypes(),
            $this->listDefinitions(),
        );

        usort(
            $records,
            static fn (array $left, array $right): int => strcmp((string) $left['name'], (string) $right['name'])
        );

        return $records;
    }

    public function findSelectableDefinition(string $id): ?array
    {
        $trimmed = trim($id);
        if ($trimmed === '') {
            return null;
        }

        foreach ($this->listSelectableDefinitions() as $record) {
            if ((string) ($record['id'] ?? '') === $trimmed) {
                return $record;
            }
        }

        return null;
    }

    public function applySelectable(string $id): array
    {
        $record = $this->findSelectableDefinition($id);
        if ($record === null) {
            throw new RuntimeException("Unknown selectable design system: {$id}");
        }

        if ((string) ($record['source'] ?? '') === 'baseline') {
            return $this->applyBaseline();
        }

        $definition = StyleDefinitionRuntime::loadJson((string) $record['path']);
        return $this->applyDefinition($definition, $record);
    }

    public function saveCurrentAsNamed(string $name, ?string $requestedId = null, ?string $description = null): array
    {
        $this->ensureStorage();

        $trimmedName = trim($name);
        if ($trimmedName === '') {
            throw new RuntimeException('Design system name is required.');
        }

        $id = $this->resolveUniqueId($requestedId ?: $trimmedName);
        $definition = $this->exportCurrentDefinition($id, $trimmedName);
        $definition['kind'] = 'named';
        if (is_string($description) && trim($description) !== '') {
            $definition['description'] = trim($description);
        } else {
            $definition['description'] = 'Saved from the currently applied design system.';
        }

        $record = $this->writeStoredDefinition($definition);
        return $record;
    }

    public function importDefinitionFile(string $inputPath, ?string $overrideName = null, bool $apply = false): array
    {
        $this->ensureStorage();

        $definition = StyleDefinitionRuntime::loadJson($inputPath);
        $tokenMap = StyleDefinitionRuntime::loadJson($this->tokenMapPath);
        $errors = Validator::validate($definition, $tokenMap, $this->myronCmsProfile());
        if ($errors !== []) {
            throw new RuntimeException("Imported definition is invalid:\n- " . implode("\n- ", $errors));
        }

        if (is_string($overrideName) && trim($overrideName) !== '') {
            $definition['name'] = trim($overrideName);
        }

        $definition['kind'] = 'named';
        $definition['id'] = $this->resolveUniqueId((string) ($definition['id'] ?? (string) $definition['name']));
        $record = $this->writeStoredDefinition($definition);

        if ($apply) {
            $record = $this->applyStored((string) $record['id']);
        }

        return $record;
    }

    public function applyStored(string $id): array
    {
        $record = $this->findDefinitionRecord($id);
        if ($record === null) {
            throw new RuntimeException("Unknown stored design system: {$id}");
        }

        $definition = StyleDefinitionRuntime::loadJson((string) $record['path']);
        return $this->applyDefinition($definition, $record);
    }

    public function applyBaseline(): array
    {
        $record = $this->baselineRecord();
        $definition = StyleDefinitionRuntime::loadJson($this->baselinePath);
        return $this->applyDefinition($definition, $record);
    }

    public function removeStored(string $id): array
    {
        $trimmedId = trim($id);
        if ($trimmedId === '') {
            throw new RuntimeException('A stored design system ID is required for removal.');
        }

        $baselineId = (string) $this->baselineRecord()['id'];
        if ($trimmedId === $baselineId) {
            throw new RuntimeException('The canonical baseline cannot be removed.');
        }

        $record = $this->findDefinitionRecord($trimmedId);
        if ($record === null) {
            throw new RuntimeException("Unknown stored design system: {$trimmedId}");
        }

        if ((string) ($record['source'] ?? 'stored') !== 'stored') {
            throw new RuntimeException('Only stored named design systems can be removed.');
        }

        $current = $this->currentDefinition();
        $removedActive = (string) ($current['id'] ?? '') === $trimmedId;
        if ($removedActive) {
            $this->applyBaseline();
        }

        $path = (string) ($record['path'] ?? '');
        if ($path === '' || !is_file($path)) {
            throw new RuntimeException('Stored design system file could not be found for removal.');
        }

        if (!unlink($path)) {
            throw new RuntimeException('Stored design system file could not be removed.');
        }

        $manifest = $this->loadManifest();
        $definitions = $manifest['definitions'];

        $definitions = array_values(array_filter(
            $definitions,
            static fn (array $entry): bool => (string) ($entry['id'] ?? '') !== $trimmedId
        ));

        $this->writeManifest($definitions, $manifest['disabledPackagedArchetypeIds']);

        return [
            'id' => $trimmedId,
            'name' => (string) ($record['name'] ?? $trimmedId),
            'removedActive' => $removedActive,
            'fallbackId' => $removedActive ? $baselineId : null,
            'fallbackName' => $removedActive ? (string) $this->baselineRecord()['name'] : null,
        ];
    }

    public function removePackagedArchetype(string $id): array
    {
        $this->ensureStorage();

        $trimmedId = trim($id);
        if ($trimmedId === '') {
            throw new RuntimeException('A system archetype ID is required for removal.');
        }

        if ($trimmedId === (string) $this->baselineRecord()['id']) {
            throw new RuntimeException('The canonical baseline cannot be removed.');
        }

        $record = null;
        foreach ($this->listPackagedArchetypes(true) as $archetype) {
            if ((string) ($archetype['id'] ?? '') === $trimmedId) {
                $record = $archetype;
                break;
            }
        }

        if ($record === null) {
            throw new RuntimeException("Unknown packaged system archetype: {$trimmedId}");
        }

        $manifest = $this->loadManifest();
        $disabledIds = $manifest['disabledPackagedArchetypeIds'];
        if (!in_array($trimmedId, $disabledIds, true)) {
            $disabledIds[] = $trimmedId;
        }

        $current = $this->currentDefinition();
        $removedActive = (string) ($current['id'] ?? '') === $trimmedId;
        if ($removedActive) {
            $this->applyBaseline();
        }

        $this->writeManifest($manifest['definitions'], $disabledIds);

        return [
            'id' => $trimmedId,
            'name' => (string) ($record['name'] ?? $trimmedId),
            'removedActive' => $removedActive,
            'fallbackId' => $removedActive ? (string) $this->baselineRecord()['id'] : null,
            'fallbackName' => $removedActive ? (string) $this->baselineRecord()['name'] : null,
        ];
    }

    /**
     * @return array{removed:list<string>, missing:list<string>, baseline:array<string, mixed>}
     */
    public function purgeStoredAndApplyBaseline(): array
    {
        $this->ensureStorage();
        $baseline = $this->applyBaseline();
        $manifest = $this->loadManifest();
        $definitions = $manifest['definitions'];

        $removed = [];
        $missing = [];
        foreach ($definitions as $entry) {
            if (!is_array($entry) || (string) ($entry['source'] ?? 'stored') !== 'stored') {
                continue;
            }

            $id = trim((string) ($entry['id'] ?? ''));
            $filename = basename((string) ($entry['filename'] ?? ''));
            if ($id === '' || $filename === '') {
                continue;
            }

            $path = $this->definitionsDir . '/' . $filename;
            if (is_file($path)) {
                if (!unlink($path)) {
                    throw new RuntimeException('Stored design system file could not be removed: ' . $filename);
                }
                $removed[] = $id;
                continue;
            }

            $missing[] = $id;
        }

        $this->writeManifest([], $manifest['disabledPackagedArchetypeIds']);

        $baseline = $this->applyBaseline();

        return [
            'removed' => $removed,
            'missing' => $missing,
            'baseline' => $baseline,
        ];
    }

    public function exportCurrentDefinition(?string $id = null, ?string $name = null): array
    {
        $tokenMap = StyleDefinitionRuntime::loadJson($this->tokenMapPath);
        $current = $this->currentDefinition();
        $definition = StyleDefinitionRuntime::loadJson((string) ($current['path'] ?? $this->baselinePath));
        return StyleDefinitionRuntime::exportCurrentDefinition(
            $tokenMap,
            $id,
            $name,
            StyleDefinitionRuntime::definitionGoogleFontsCss2Url($definition)
        );
    }

    public function exportCurrentJson(?string $id = null, ?string $name = null): string
    {
        return StyleDefinitionRuntime::jsonEncodePretty($this->exportCurrentDefinition($id, $name));
    }

    /**
     * @return array{containers:array<string, string>, gutters:array<string, string>, breakpoints:array<string, string>, widthModes:array<string, array{mode:string, container:string}>}
     */
    public function currentLayoutTokens(): array
    {
        $current = $this->currentDefinition();
        $definition = StyleDefinitionRuntime::loadJson((string) $current['path']);
        $layout = is_array($definition['tokens']['layout'] ?? null) ? $definition['tokens']['layout'] : [];

        return [
            'containers' => $this->layoutTokenGroup($layout, 'containers'),
            'gutters' => $this->layoutTokenGroup($layout, 'gutters'),
            'breakpoints' => $this->layoutTokenGroup($layout, 'breakpoints'),
            'widthModes' => $this->layoutWidthModes($layout),
        ];
    }

    /**
     * @param array{containers?:array<string, mixed>, gutters?:array<string, mixed>, breakpoints?:array<string, mixed>, widthModes?:array<string, mixed>} $layout
     */
    public function updateCurrentLayoutTokens(array $layout): array
    {
        $this->ensureStorage();

        $current = $this->currentDefinition();
        $definition = StyleDefinitionRuntime::loadJson((string) $current['path']);

        foreach (['containers', 'gutters', 'breakpoints'] as $group) {
            if (!is_array($layout[$group] ?? null)) {
                throw new RuntimeException("Layout token group `{$group}` is required.");
            }
            foreach (['sm', 'md', 'lg'] as $key) {
                $value = trim((string) ($layout[$group][$key] ?? ''));
                if ($value === '') {
                    throw new RuntimeException("Layout token `{$group}.{$key}` is required.");
                }
                $definition['tokens']['layout'][$group][$key] = $value;
            }
        }
        $incomingWidthModes = is_array($layout['widthModes'] ?? null) ? $layout['widthModes'] : $this->layoutWidthModes(is_array($definition['tokens']['layout'] ?? null) ? $definition['tokens']['layout'] : []);
        foreach (['page', 'section'] as $scope) {
            $intent = is_array($incomingWidthModes[$scope] ?? null) ? $incomingWidthModes[$scope] : [];
            $mode = trim((string) ($intent['mode'] ?? 'contained'));
            $container = trim((string) ($intent['container'] ?? 'lg'));
            if (!in_array($mode, ['contained', 'full-bleed', 'fixed', 'mixed'], true)) {
                throw new RuntimeException("Layout width mode `{$scope}.mode` is invalid.");
            }
            if (!in_array($container, ['sm', 'md', 'lg'], true)) {
                throw new RuntimeException("Layout width mode `{$scope}.container` must reference sm, md, or lg.");
            }
            $definition['tokens']['layout']['widthModes'][$scope] = [
                'mode' => $mode,
                'container' => $container,
            ];
        }

        $definition['kind'] = 'named';
        $tokenMap = StyleDefinitionRuntime::loadJson($this->tokenMapPath);
        $errors = Validator::validate($definition, $tokenMap, $this->myronCmsProfile());
        if ($errors !== []) {
            throw new RuntimeException("Layout settings are invalid:\n- " . implode("\n- ", $errors));
        }

        if ((string) ($current['source'] ?? '') === 'baseline') {
            $definition['id'] = $this->resolveUniqueId((string) ($definition['id'] ?? 'apollo-v0.1.0') . '-layout-settings');
            $definition['name'] = 'Apollo Layout Settings';
            $definition['description'] = 'Named design system generated from governed layout settings.';
            $record = $this->writeStoredDefinition($definition);
        } else {
            $definition['id'] = (string) ($current['id'] ?? $definition['id'] ?? 'active-layout-settings');
            $definition['name'] = (string) ($current['name'] ?? $definition['name'] ?? 'Active Layout Settings');
            $definition['description'] = (string) ($current['description'] ?? $definition['description'] ?? 'Updated from governed layout settings.');
            $record = $this->writeStoredDefinition($definition);
        }

        return $this->applyDefinition($definition, $record);
    }

    /**
     * @return array<string, array{mode:string, container:string}>
     */
    private function layoutWidthModes(array $layout): array
    {
        $widthModes = is_array($layout['widthModes'] ?? null) ? $layout['widthModes'] : [];
        $resolved = [];
        foreach (['page', 'section'] as $scope) {
            $intent = is_array($widthModes[$scope] ?? null) ? $widthModes[$scope] : [];
            $mode = (string) ($intent['mode'] ?? 'contained');
            $container = (string) ($intent['container'] ?? 'lg');
            $resolved[$scope] = [
                'mode' => in_array($mode, ['contained', 'full-bleed', 'fixed', 'mixed'], true) ? $mode : 'contained',
                'container' => in_array($container, ['sm', 'md', 'lg'], true) ? $container : 'lg',
            ];
        }

        return $resolved;
    }

    private function applyDefinition(array $definition, array $record): array
    {
        $tokenMap = StyleDefinitionRuntime::loadJson($this->tokenMapPath);
        $profile = $this->myronCmsProfile();
        $target = new MyronCssTarget();
        $compiledTokens = $target->transpile($definition, $tokenMap, $profile);
        $compiledFonts = $target->transpileFontRoles($definition, $profile);

        StyleDefinitionRuntime::writeFile($this->runtimeTokensPath, $compiledTokens);
        StyleDefinitionRuntime::writeFile($this->runtimeFontsPath, $compiledFonts);

        $pointer = $this->hydrateRecord($record);
        $pointer['appliedAt'] = $this->now();

            StyleDefinitionRuntime::writeFile(
                $this->currentDefinitionPath,
                StyleDefinitionRuntime::jsonEncodePretty($this->portableRecord($pointer))
            );

        if (function_exists('myronInvalidatePublicPageCache')) {
            myronInvalidatePublicPageCache('design system applied');
        }

            return $pointer;
        }

    private function writeStoredDefinition(array $definition): array
    {
        $id = (string) $definition['id'];
        $filename = $id . '.jsonm';
        $path = $this->definitionsDir . '/' . $filename;
        $now = $this->now();

        StyleDefinitionRuntime::writeFile($path, StyleDefinitionRuntime::jsonEncodePretty($definition));

        $manifest = $this->loadManifest();
        $definitions = $manifest['definitions'];

        $updated = false;
        foreach ($definitions as &$entry) {
            if (($entry['id'] ?? null) === $id) {
                $entry['name'] = (string) $definition['name'];
                $entry['kind'] = (string) $definition['kind'];
                $entry['description'] = (string) $definition['description'];
                $entry['filename'] = $filename;
                $entry['source'] = 'stored';
                $entry['updatedAt'] = $now;
                $updated = true;
                break;
            }
        }
        unset($entry);

        if (!$updated) {
            $definitions[] = [
                'id' => $id,
                'name' => (string) $definition['name'],
                'kind' => (string) $definition['kind'],
                'description' => (string) $definition['description'],
                'filename' => $filename,
                'source' => 'stored',
                'createdAt' => $now,
                'updatedAt' => $now,
            ];
        }

        usort(
            $definitions,
            static fn (array $left, array $right): int => strcmp((string) $left['name'], (string) $right['name'])
        );

        $this->writeManifest($definitions, $manifest['disabledPackagedArchetypeIds']);

        return $this->hydrateRecord([
            'id' => $id,
            'name' => (string) $definition['name'],
            'kind' => (string) $definition['kind'],
            'description' => (string) $definition['description'],
            'filename' => $filename,
            'source' => 'stored',
            'createdAt' => $updated ? $this->findDefinitionRecord($id)['createdAt'] ?? $now : $now,
            'updatedAt' => $now,
        ]);
    }

    /**
     * @param array<string, mixed> $layout
     * @return array<string, string>
     */
    private function layoutTokenGroup(array $layout, string $group): array
    {
        $values = is_array($layout[$group] ?? null) ? $layout[$group] : [];

        return [
            'sm' => (string) ($values['sm'] ?? ''),
            'md' => (string) ($values['md'] ?? ''),
            'lg' => (string) ($values['lg'] ?? ''),
        ];
    }

    private function findDefinitionRecord(string $id): ?array
    {
        foreach ($this->listDefinitions() as $record) {
            if ((string) ($record['id'] ?? '') === $id) {
                return $record;
            }
        }

        return null;
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function listPackagedArchetypes(bool $includeDisabled = false): array
    {
        $this->ensureStorage();

        if (!is_dir($this->archetypesDir)) {
            return [];
        }

        $paths = glob($this->archetypesDir . '/*.jsonm') ?: [];
        sort($paths, SORT_STRING);
        $records = [];
        $disabledIds = $includeDisabled ? [] : array_fill_keys($this->loadManifest()['disabledPackagedArchetypeIds'], true);

        foreach ($paths as $path) {
            $definition = StyleDefinitionRuntime::loadJson((string) $path);
            $id = trim((string) ($definition['id'] ?? ''));
            $name = trim((string) ($definition['name'] ?? ''));
            if ($id === '' || $name === '') {
                continue;
            }
            if (isset($disabledIds[$id])) {
                continue;
            }

            $records[] = $this->hydrateRecord([
                'id' => $id,
                'name' => $name,
                'kind' => 'archetype',
                'description' => (string) ($definition['description'] ?? 'Packaged archetype design system.'),
                'filename' => basename((string) $path),
                'source' => 'archetype',
                'path' => (string) $path,
                'createdAt' => $this->fileTimestamp((string) $path),
                'updatedAt' => $this->fileTimestamp((string) $path),
            ]);
        }

        usort(
            $records,
            static fn (array $left, array $right): int => strcasecmp((string) ($left['name'] ?? ''), (string) ($right['name'] ?? ''))
        );

        return $records;
    }

    /**
     * @return array{definitions:list<array<string, mixed>>, disabledPackagedArchetypeIds:list<string>}
     */
    private function loadManifest(): array
    {
        $manifest = StyleDefinitionRuntime::loadJson($this->manifestPath);
        $definitions = $manifest['definitions'] ?? [];
        if (!is_array($definitions)) {
            throw new RuntimeException('Definitions manifest is invalid.');
        }

        $disabled = $manifest['disabledPackagedArchetypeIds'] ?? [];
        if (!is_array($disabled)) {
            $disabled = [];
        }

        $disabledIds = [];
        foreach ($disabled as $id) {
            $trimmed = trim((string) $id);
            if ($trimmed !== '') {
                $disabledIds[$trimmed] = true;
            }
        }

        return [
            'definitions' => array_values($definitions),
            'disabledPackagedArchetypeIds' => array_keys($disabledIds),
        ];
    }

    /**
     * @param list<array<string, mixed>> $definitions
     * @param list<string> $disabledPackagedArchetypeIds
     */
    private function writeManifest(array $definitions, array $disabledPackagedArchetypeIds): void
    {
        $disabledIds = [];
        foreach ($disabledPackagedArchetypeIds as $id) {
            $trimmed = trim((string) $id);
            if ($trimmed !== '') {
                $disabledIds[$trimmed] = true;
            }
        }

        StyleDefinitionRuntime::writeFile(
            $this->manifestPath,
            StyleDefinitionRuntime::jsonEncodePretty([
                'definitions' => array_values($definitions),
                'disabledPackagedArchetypeIds' => array_keys($disabledIds),
            ])
        );
    }

    private function hydrateRecord(array $entry): array
    {
        $source = (string) ($entry['source'] ?? 'stored');
        $filename = (string) ($entry['filename'] ?? '');
        $path = (string) ($entry['path'] ?? '');

        if ($path === '') {
            $path = match ($source) {
                'baseline' => $this->baselinePath,
                'archetype' => $this->archetypesDir . '/' . $filename,
                default => $this->definitionsDir . '/' . $filename,
            };
        }

        return [
            'id' => (string) ($entry['id'] ?? ''),
            'name' => (string) ($entry['name'] ?? ''),
            'kind' => (string) ($entry['kind'] ?? 'named'),
            'description' => (string) ($entry['description'] ?? ''),
            'source' => $source,
            'filename' => $filename !== '' ? $filename : basename($path),
            'path' => $path,
            'createdAt' => $entry['createdAt'] ?? $this->fileTimestamp($path),
            'updatedAt' => $entry['updatedAt'] ?? $this->fileTimestamp($path),
            'appliedAt' => $entry['appliedAt'] ?? null,
        ];
    }

    private function portableRecord(array $record): array
    {
        return [
            'id' => (string) ($record['id'] ?? ''),
            'name' => (string) ($record['name'] ?? ''),
            'kind' => (string) ($record['kind'] ?? 'named'),
            'description' => (string) ($record['description'] ?? ''),
            'source' => (string) ($record['source'] ?? 'stored'),
            'filename' => (string) ($record['filename'] ?? ''),
            'createdAt' => $record['createdAt'] ?? null,
            'updatedAt' => $record['updatedAt'] ?? null,
            'appliedAt' => $record['appliedAt'] ?? null,
        ];
    }

    private function resolveUniqueId(string $raw): string
    {
        $candidate = $this->slugify($raw);
        $id = $candidate;
        $counter = 2;

        while ($this->definitionIdExists($id)) {
            $id = $candidate . '-' . $counter;
            $counter++;
        }

        return $id;
    }

    private function definitionIdExists(string $id): bool
    {
        if ($id === (string) $this->baselineRecord()['id']) {
            return true;
        }

        foreach ($this->listDefinitions() as $record) {
            if ((string) ($record['id'] ?? '') === $id) {
                return true;
            }
        }

        return false;
    }

    private function slugify(string $value): string
    {
        $normalized = strtolower(trim($value));
        $slug = preg_replace('/[^a-z0-9]+/', '-', $normalized);
        $slug = trim((string) $slug, '-');

        return $slug !== '' ? $slug : 'design-system';
    }

    private function fileTimestamp(string $path): string
    {
        $timestamp = is_file($path) ? filemtime($path) : time();
        if (!is_int($timestamp) || $timestamp <= 0) {
            $timestamp = time();
        }

        return date(DATE_ATOM, $timestamp);
    }

    private function now(): string
    {
        return date(DATE_ATOM);
    }
}
