<?php

declare(strict_types=1);

namespace apexl\Io\collections;

use apexl\Io\dto\LoaderCondition;
use apexl\Io\dto\LoaderConditionRaw;
use apexl\Io\interfaces\LoaderConditionInterface;
use apexl\Vault\enums\WhereType;
use apexl\Vault\interfaces\ConditionType;
use apexl\Vault\interfaces\driver;
use Arrayy\Collection\AbstractCollection;
use Arrayy\Type\TypeInterface;

/**
 * @extends AbstractCollection<array-key, LoaderConditionInterface>
 */
class LoaderConditionCollection extends AbstractCollection implements LoaderConditionInterface, TypeInterface
{
    private bool $shouldWrap = true;
    private ConditionType $whereType = WhereType::AND;

    public function getType(): string
    {
        return LoaderConditionInterface::class;
    }

    public function setType(ConditionType $whereType): LoaderConditionCollection
    {
        $this->whereType = $whereType;

        return $this;
    }

    public function whereType(): ConditionType
    {
        return $this->whereType;
    }

    public function applyToQuery(driver $query): driver
    {
        if ($this->isEmpty()) {
            return $query;
        }

        $query->startWhereGroup();

        $this->each(
            function (LoaderConditionInterface $condition) use (&$query) {
                $query = $this->normaliseConditionType($condition)->applyToQuery($query);
            }
        );

        $query->endWhereGroup();

        return $query;
    }

    private function normaliseConditionType(LoaderConditionInterface $condition): LoaderConditionInterface
    {
        if ($condition instanceof LoaderConditionCollection || $condition instanceof LoaderConditionRaw) {
            return $condition;
        }

        if ($condition instanceof LoaderCondition) {
            return $condition->with('type', $this->whereType);
        }

        return $condition;
    }

    public function shouldWrap(): bool
    {
        if (!$this->shouldWrap) {
            return false;
        }

        foreach ($this as $loaderCondition) {
            if ($loaderCondition->shouldWrap()) {
                return true;
            }
        }

        return false;
    }

    public function setShouldWrap(bool $shouldWrap): void
    {
        $this->shouldWrap = $shouldWrap;
    }
}
