<?php

/**
 * Validated parameters for the custom_table endpoint.
 */
class CustomTableQuery
{
    private const IDENTIFIER = '/^[A-Za-z_][A-Za-z0-9_]*$/';

    private const MAX_LIMIT = 1000;

    public function __construct(
        public readonly string $table,
        public readonly array $includes = [],
        public readonly int $page = 1,
        public readonly int $limit = 10,
        public readonly string $sortColumn = '',
        public readonly string $sortDescending = '0',
    ) {}

    /**
     * The Query API pages by row offset rather than page number.
     */
    public function skip(): int
    {
        return ($this->page - 1) * $this->limit;
    }

    /**
     * @throws InvalidArgumentException when the posted payload is unusable
     */
    public static function fromPost(array $post): self
    {
        $table = trim((string) ($post['table'] ?? ''));
        if (!preg_match(self::IDENTIFIER, $table)) {
            throw new InvalidArgumentException('table must be a plain table name');
        }

        $sortColumn = trim((string) ($post['sortColumn'] ?? ''));
        if ($sortColumn !== '' && !preg_match(self::IDENTIFIER, $sortColumn)) {
            throw new InvalidArgumentException('sortColumn must be a plain column name');
        }

        $includes = $post['includes'] ?? [];
        if (!is_array($includes)) {
            throw new InvalidArgumentException('includes must be an array');
        }

        $page = filter_var($post['page'] ?? 1, FILTER_VALIDATE_INT);
        if ($page === false || $page < 1) {
            throw new InvalidArgumentException('page must be a positive integer');
        }

        $limit = filter_var($post['limit'] ?? 10, FILTER_VALIDATE_INT);
        if ($limit === false || $limit < 1 || $limit > self::MAX_LIMIT) {
            throw new InvalidArgumentException('limit must be between 1 and ' . self::MAX_LIMIT);
        }

        return new self(
            $table,
            array_values(array_map('strval', $includes)),
            $page,
            $limit,
            $sortColumn,
            empty($post['sortDescending']) ? '0' : '1',
        );
    }
}
