<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator\Services;

use apexl\Io\dto\LoaderCondition;
use apexl\Io\exceptions\LoaderConditionResolutionException;
use apexl\Io\exceptions\TabulatorFilterConditionCannotBeConvertedToWhereException;
use apexl\Io\modules\tabulator\Collections\TabulatorFilterCollection;
use apexl\Io\modules\tabulator\Collections\TabulatorSortCollection;
use apexl\Io\modules\tabulator\Dto\TabulatorData;
use apexl\Io\modules\tabulator\Interfaces\HasSubEntitiesInterface;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\modules\tabulator\Interfaces\HasTreeStructureInterface;
use apexl\Vault\enums\WhereCondition;
use Arrayy\Type\ArrayCollection;

readonly final class TabulatorDataService
{
    public function __construct(private TabulatorTreeBuilderService $treeBuilderService) {}

    /**
     * @throws LoaderConditionResolutionException
     * @throws TabulatorFilterConditionCannotBeConvertedToWhereException
     */
    public function get(
        HasTabulatorViewInterface $entity,
        TabulatorFilterCollection $filters,
        TabulatorSortCollection $sort,
        int $size,
        int $page,
        string|null $subEntityKey = null
    ): TabulatorData {
        $conditions = $filters->toLoaderConditions($entity);

        $sorts = $sort->toLoaderSortCollection($entity);

        $wantedFields = [];
        if ($subEntityKey && $entity instanceof HasSubEntitiesInterface) {
            $wantedFields = $entity->getSubEntityFieldsByKey($subEntityKey);
            //apply filters to sub-entity
            $subEntityCondition = new LoaderCondition(
                column: $entity->subEntityDerivedColumn(),
                operator: WhereCondition::EQUALS,
                value: $subEntityKey,
            );
            $conditions[] = $subEntityCondition;
        }
        $fields = $entity->tabulatable()->fields(wanted: $wantedFields);

        $offset = ($page - 1) * $size;

        $limit = ($entity instanceof HasTreeStructureInterface) ? null : $size;

        $data = ($entity)::findMultiple(
            conditions: $conditions,
            limit: $limit,
            offset: $offset,
            sorts: $sorts,
            fields: $fields,
            paging: $paging,
        );

        $data = ArrayCollection::createFromObject(
            $data->map(
                fn(HasTabulatorViewInterface $entity): array => $entity->tabulate(
                    $subEntityKey ? $fields : $entity->tabulatable()->fields(includeDataFields: false)
                )
            )
        );

        if ($entity instanceof HasTreeStructureInterface) {
            $data = $this->treeBuilderService->buildTree($data->toArray());
        }

        [$totalRows, $totalPages] = $paging;

        return new TabulatorData(
            $data,
            (int) $totalPages,
            (int) $totalRows
        );
    }
}
