<?php

declare(strict_types=1);

final class MicTableService
{
    private const ALLOWED_KEYS = [
        'name' => true,
        'description' => true,
        'caption' => true,
        'columns' => true,
        'rows' => true,
        'header_style' => true,
        'responsive_strategy' => true,
    ];

    public function __construct(private readonly MicTableRepository $repository)
    {
    }

    public function list(int $limit = 100, int $offset = 0): array
    {
        $tables = $this->repository->listAll($limit, $offset);
        foreach ($tables as $index => $table) {
            $tables[$index]['referencesCount'] = count($this->repository->findReferencingPages((string) ($table['id'] ?? '')));
        }
        return ['tables' => $tables];
    }

    public function read(string $tableId): array
    {
        $table = $this->repository->findById($tableId);
        if ($table === null) {
            throw new InvalidArgumentException('Table not found.');
        }
        $references = $this->repository->findReferencingPages($tableId);
        return ['table' => $table, 'references' => $references, 'referencesCount' => count($references)];
    }

    public function create(array $input, MyronActionCallerContext $caller): array
    {
        $payload = $this->filterPayload($input, true);
        $payload['created_by'] = $caller->callerId;
        $payload['updated_by'] = $caller->callerId;
        return ['table' => $this->repository->create($payload)];
    }

    public function update(string $tableId, array $input, MyronActionCallerContext $caller): array
    {
        $payload = $this->filterPayload($input, false);
        $payload['updated_by'] = $caller->callerId;
        return ['table' => $this->repository->update($tableId, $payload)];
    }

    public function delete(string $tableId): array
    {
        if ($this->repository->findById($tableId) === null) {
            throw new InvalidArgumentException('Table not found.');
        }
        return ['tableId' => $tableId, 'deleted' => $this->repository->delete($tableId)];
    }

    public function duplicate(string $tableId, MyronActionCallerContext $caller): array
    {
        $existing = $this->repository->findById($tableId);
        if ($existing === null) {
            throw new InvalidArgumentException('Table not found.');
        }
        $existing['name'] = (string) ($existing['name'] ?? 'Table') . ' (copy)';
        $existing['created_by'] = $caller->callerId;
        $existing['updated_by'] = $caller->callerId;
        return ['table' => $this->repository->create($existing)];
    }

    private function filterPayload(array $input, bool $creating): array
    {
        $payload = [];
        foreach ($input as $key => $value) {
            if (!isset(self::ALLOWED_KEYS[(string) $key])) {
                if (in_array((string) $key, ['tableId', 'action', 'csrf_token'], true)) {
                    continue;
                }
                throw new InvalidArgumentException('Unknown table field: ' . (string) $key . '.');
            }
            $payload[(string) $key] = $value;
        }
        if ($creating && trim((string) ($payload['name'] ?? '')) === '') {
            throw new InvalidArgumentException('Table name is required.');
        }
        return $payload;
    }
}

