<?php

declare(strict_types=1);

namespace PpPhp\Core\Events;

/**
 * Canonical contract for events bound to one table or database view.
 */
final class TableEvent
{
    /** @var array<string, mixed> */
    private array $groups;

    /** @var array<string, mixed> */
    private array $fieldGroups;

    /**
     * @param array<int, string> $fields
     * @param array<string, mixed>|null $groups
     * @param array<string, mixed>|null $fieldGroups
     */
    public function __construct(
        private readonly string $table,
        private readonly array $fields = [],
        ?array $groups = null,
        ?array $fieldGroups = null
    ) {
        $this->groups = EventCatalogue::enrich($groups ?? EventCatalogue::tableGroups());
        $this->fieldGroups = EventCatalogue::enrich($fieldGroups ?? EventCatalogue::fieldGroups());
    }

    /** @return array<string, mixed> */
    public function groups(): array
    {
        return $this->groups;
    }

    /** @return array<string, mixed> */
    public function fieldGroups(): array
    {
        return $this->fieldGroups;
    }

    /** @return array<string, mixed>|null */
    public function definition(
        string $group,
        string $event,
        ?string $field = null,
        ?string $fieldGroup = null
    ): ?array {
        if ($group === 'fields') {
            $resolvedFieldGroup = $fieldGroup ?? 'server';
            $definition = $this->fieldGroups[$resolvedFieldGroup]['events'][$event] ?? null;
            $groupLanguage = $this->fieldGroups[$resolvedFieldGroup]['lang'] ?? null;
        } else {
            $definition = $this->groups[$group]['events'][$event] ?? null;
            $groupLanguage = $this->groups[$group]['lang'] ?? null;
        }

        if (!is_array($definition)) {
            return null;
        }
        if (!isset($definition['lang']) && is_string($groupLanguage)) {
            $definition['lang'] = $groupLanguage;
        }

        return $definition;
    }

    /** @return array<string, mixed> */
    public function describe(
        string $group,
        string $event,
        ?string $language = null,
        ?string $field = null,
        ?string $fieldGroup = null
    ): array {
        $definition = $this->definition($group, $event, $field, $fieldGroup) ?? [];
        $resolvedLanguage = $language
            ?? (string) ($definition['lang'] ?? EventCatalogue::LANG_PHP);

        return [
            'scope' => 'table',
            'table' => $this->table,
            'field' => $field,
            'group' => $group,
            'fieldGroup' => $fieldGroup,
            'event' => $event,
            'label' => (string) ($definition['label'] ?? $event),
            'language' => $resolvedLanguage,
            'signature' => $this->signature($event, $definition),
            'description' => (string) ($definition['desc'] ?? ''),
            'arguments' => (array) ($definition['args'] ?? []),
            'returns' => (string) ($definition['returns'] ?? 'void'),
        ];
    }

    /** @return array<int, array<string, mixed>> */
    public function intellisense(
        string $group,
        string $event,
        ?string $language = null,
        ?string $field = null,
        ?string $fieldGroup = null
    ): array {
        $definition = $this->definition($group, $event, $field, $fieldGroup) ?? [];
        $resolvedLanguage = $language
            ?? (string) ($definition['lang'] ?? EventCatalogue::LANG_PHP);

        return EventIntellisense::suggestions($definition, $resolvedLanguage, $this->fields, [
            'scope' => 'table',
            'table' => $this->table,
            'field' => $field,
            'group' => $group,
            'fieldGroup' => $fieldGroup,
            'event' => $event,
        ]);
    }

    /** @param array<string, mixed> $definition */
    private function signature(string $event, array $definition): string
    {
        $arguments = array_keys((array) ($definition['args'] ?? []));
        return $event . '(' . implode(', ', array_map('strval', $arguments)) . ')';
    }
}
