<?php

declare(strict_types=1);

namespace apexl\Io\modules\tabulator\Dto;

use apexl\dto\Dto;
use apexl\Io\exceptions\LoaderConditionResolutionException;
use apexl\Io\exceptions\TabulatorFilterConditionCannotBeConvertedToWhereException;
use apexl\Io\includes\System;
use apexl\Io\interfaces\LoaderConditionInterface;
use apexl\Io\modules\tabulator\Collections\TabulatorFilterCollection;
use apexl\Io\modules\tabulator\Enums\TabulatorFilterCondition;
use apexl\Io\modules\tabulator\Interfaces\HasTabulatorViewInterface;
use apexl\Io\modules\tabulator\Services\TabulatorFilterToLoaderConditionResolver;
use BackedEnum;
use JsonSerializable;
use UnitEnum;

final readonly class TabulatorFilter extends Dto implements JsonSerializable
{
    public TabulatorFilterCollection|string|array|bool|int|UnitEnum $value;

    public function __construct(
        public string $field,
        bool|string|int|array|TabulatorFilterCollection|UnitEnum $value,
        public TabulatorFilterCondition $condition = TabulatorFilterCondition::EXACT,
        private ?TabulatorColumnDto $column = null,
    ) {
        $this->value = $this->resolveValue($value);
    }

    private function resolveValue(UnitEnum|array|bool|int|string|TabulatorFilterCollection $value)
    {
        if ($value instanceof BackedEnum) {
            return $value->value;
        }

        if ($value instanceof UnitEnum) {
            return $value->name;
        }

        return $value;
    }

    public static function fromArray(array $filter): TabulatorFilter
    {
        $condition = $filter['type'] ?? TabulatorFilterCondition::EXACT;
        if (!$condition instanceof TabulatorFilterCondition) {
            $condition = TabulatorFilterCondition::from($condition);
        }

        $value = $filter['value'];

        if (is_array($value)) {
            $value = match ($condition) {
                TabulatorFilterCondition::BETWEEN, TabulatorFilterCondition::CREATOR, TabulatorFilterCondition::BEATING_HEART => $value,
                default => TabulatorFilterCollection::createFromArray($value),
            };
        }

        return new TabulatorFilter(
            field: $filter['field'],
            value: $value,
            condition: $condition,
        );
    }

    public function jsonSerialize(): array
    {
        return $this->toArray();
    }

    /**
     * @throws LoaderConditionResolutionException
     * @throws TabulatorFilterConditionCannotBeConvertedToWhereException
     */
    public function toLoaderCondition(HasTabulatorViewInterface $entity): LoaderConditionInterface|array
    {
        $loaderConditionResolver = System::makeRegisteredService(TabulatorFilterToLoaderConditionResolver::class, [
            'filter' => $this,
            'entity' => $entity,
            'columnDto' => $this->column,
        ]);

        return $loaderConditionResolver->resolve();
    }

}
