<?php

declare(strict_types=1);

namespace apexl\Io\services;

use apexl\Io\collections\LoaderConditionCollection;
use apexl\Io\collections\LoaderSortCollection;
use apexl\Io\includes\Entity;
use apexl\Io\modules\tabulator\Services\TabulatorFieldRenamer;
use Arrayy\Type\StringCollection;

final readonly class LoaderArgumentResolver
{
    private string|array|StringCollection $fields;

    public function __construct(
        string|array|StringCollection $fields,
        private ?string $table,
        private Entity $entity,
        private LoaderSortResolver $loaderSortResolver,
        private LoaderDataFieldsResolver $loaderDataFieldsResolver,
        private TabulatorFieldRenamer $tabulatorFieldRenamer,
    ) {
        $this->fields = $this->resolveFields($fields);
    }

    private function resolveFields(string|array|StringCollection $fields): StringCollection
    {
        if (is_string($fields)) {
            $fields = [$fields];
        }

        if (is_array($fields)) {
            $fields = StringCollection::createFromArray($fields);
        }

        return $fields->map($this->tabulatorFieldRenamer->rename(...));
    }

    public function resolveSorts(array|string|LoaderSortCollection $sorts): LoaderSortCollection
    {
        return $this->loaderSortResolver->resolve($sorts);
    }

    public function resolveDataFields(bool $autoIncludePrimaryKey = true): StringCollection
    {
        return $this->loaderDataFieldsResolver
            ->resolve($this->table, $this->fields, $this->entity, $autoIncludePrimaryKey);
    }

    public function resolveVirtualFields(): StringCollection
    {
        $output = [];
        $this->fields
            ->filter($this->entity->isVirtualField(...))
            ->each(function (string $field) use (&$output): void {
                $_field = $this->entity->virtualFields()->get($field);
                $output[$_field->field] = $_field->toSelect($this->table);
            });

        return StringCollection::createFromArray($output)
            ->flip()
            ->map(fn(string $field): string => sprintf('`%s`', $field));
    }

    public function resolveRelations(): StringCollection
    {
        $filtered = $this->fields
            ->filter($this->entity->isRelationField(...))
            ->unique();

        $grouped = $filtered->group(
            function (string $field): string {
                if (str_contains($field, '__')) {
                    return substr($field, 0, strpos($field, '__') ?: 0);
                }

                return $field;
            }
        )->map(
            fn(array $fields): array => array_map(
                fn(string $field): string => str_contains($field, '__') ?
                    substr($field, strpos($field, '__') + 2) :
                    $field,
                $fields
            )
        );

        $mapped = $grouped->map(
            function (array $fields, $key): string {
                $relation = $this->entity->relation($key);

                return sprintf(
                    '(%s)',
                    $relation->subSelectString($fields, $this->table),
                );
            },
            true,
        );

        return StringCollection::createFromObject($mapped)
            ->flip()
            ->map(fn(string $field): string => sprintf('`%s`', $field));
    }

    public function resolveConditions(array|LoaderConditionCollection $conditions): LoaderConditionCollection
    {
        if (is_array($conditions)) {
            $conditions = LoaderConditionCollection::createFromArray($conditions);
        }

        return $conditions;
    }

    public function resolveLimit(?int $limit, int $offset): ?int
    {
        if ($limit === null) {
            return $offset ? PHP_INT_MAX : $limit;
        }

        return $limit;
    }
}