<?php

declare(strict_types=1);

namespace apexl\Io\services;

use apexl\Io\dto\LoaderCondition;
use apexl\Io\exceptions\LoaderConditionResolutionException;
use apexl\Io\exceptions\TabulatorFilterConditionCannotBeConvertedToWhereException;
use apexl\Io\includes\Entity;
use apexl\Io\includes\System;
use apexl\Io\interfaces\LoaderConditionInterface;
use apexl\Io\modules\tabulator\Dto\TabulatorColumnDto;
use apexl\Io\modules\tabulator\Dto\TabulatorFilter;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\modules\tabulator\Services\TabulatorColumnFieldNameResolver;


final readonly class LoaderConditionResolver
{
    private ?TabulatorColumnDto $columnDto;
    private string $field;
    private TabulatorColumnFieldNameResolver $tabulatorColumnFieldNameResolver;

    public function __construct(
        private TabulatorFilter $filter,
        private Entity $entity,
        ?TabulatorColumnDto $columnDto = null,
    ) {
        $this->tabulatorColumnFieldNameResolver = System::makeRegisteredService(
            TabulatorColumnFieldNameResolver::class,
            [
                'entity' => $this->entity,
            ]
        );
        if (!$columnDto && $this->entity instanceof HasTabulatorViewInterface) {
            $columnDto = $this->entity->tabulatable()->column($this->filter->field);
        }
        $this->columnDto = $columnDto;
        $this->field = $this->columnDto->filterOn ?? $this->filter->field;
    }


    /**
     * @return LoaderConditionInterface
     * @throws LoaderConditionResolutionException
     * @throws TabulatorFilterConditionCannotBeConvertedToWhereException
     */
    public function resolve(): LoaderConditionInterface
    {
        if ($this->field === '_general') {
            $generalSearchLoaderConditionCollectionBuilder = System::makeRegisteredService(
                GeneralSearchLoaderConditionCollectionBuilder::class,
                [
                    'entity' => $this->entity,
                ]
            );

            return $generalSearchLoaderConditionCollectionBuilder->build($this->filter->value);
        }

        if ($this->columnDto->loaderConditionCallback) {
            $relation = str_contains($this->field, '__') ? $this->entity->relation(
                strstr($this->field, '__', true)
            ) : null;

            $filter = $this->filter->with(
                'field',
                $relation ? $relation->subSelectStringSingleField(substr(strstr($this->field, '__'), 2)) :
                    $this->tabulatorColumnFieldNameResolver->resolve(
                        $this->field,
                        $this->entity->relation($this->field)
                    )
            );

            return ($this->columnDto->loaderConditionCallback)(
                $filter,
            );
        }

        $relation = str_contains($this->field, '__') ? $this->entity->relation(
            strstr($this->field, '__', true)
        ) : null;

        if ($relation) {
            $field = substr(strstr($this->field, '__'), 2);

            return $relation->loaderConditionForField(
                $field,
                $this->filter->condition->toWhereCondition(),
                $this->filter->value
            );
        }

        $field = $this->field($this->field);

        if ($this->tabulatorColumnFieldNameResolver->isLocalDataField($field)) {
            return $this->forLocalDataField($field);
        }

        if ($this->tabulatorColumnFieldNameResolver->isLocalVirtualField($this->field)) {
            return $this->forLocalVirtualField($this->field);
        }


        throw new LoaderConditionResolutionException(
            sprintf(
                'Could not resolve loader condition from %s for %s',
                $this->field,
                $this->entity::class
            )
        );
    }

    private function field($field): string
    {
        $field = explode('__', $field);

        return $field[count($field) - 1];
    }

    /**
     * @throws TabulatorFilterConditionCannotBeConvertedToWhereException
     */
    private function forLocalDataField(string $field): LoaderConditionInterface
    {
        return new LoaderCondition(
            $this->tabulatorColumnFieldNameResolver->resolve($field),
            $this->filter->condition->toWhereCondition(),
            $this->filter->value,
        );

    }

    /**
     * @throws TabulatorFilterConditionCannotBeConvertedToWhereException
     */
    private function forLocalVirtualField(string $field): LoaderConditionInterface
    {
        return new LoaderCondition(
            $this->tabulatorColumnFieldNameResolver->resolve($field),
            $this->filter->condition->toWhereCondition(),
            $this->filter->value,
        );
    }
}