<?php

declare(strict_types=1);

namespace apexl\Io\dto;

use apexl\dto\Dto;
use apexl\dto\Raw;
use apexl\Io\interfaces\LoaderConditionInterface;
use apexl\Vault\enums\WhereCondition;
use apexl\Vault\enums\WhereType;
use apexl\Vault\interfaces\ConditionType;
use apexl\Vault\interfaces\driver;

readonly class LoaderCondition extends Dto implements LoaderConditionInterface
{
    public function __construct(
        public string $column,
        public WhereCondition $operator,
        public int|string|float|array|bool|null $value = null,
        public ConditionType $type = WhereType::AND,
        private bool $shouldWrap = true,
        private bool $parameteriseValue = true,
    ) {}

    public static function isNull(string $column): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::IS_NULL
        );
    }

    public static function notNull(string $column): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::IS_NOT_NULL
        );
    }

    public static function equals(string $column, int|string|float|bool $value): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::EQUALS,
            value: $value,
        );
    }

    public static function notEquals(string $column, int|string|float $value): LoaderCondition
    {
        return new LoaderCondition(

            column: $column,
            operator: WhereCondition::NOT_EQUALS,
            value: $value,
        );
    }

    public static function in(string $column, array $in): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::IN,
            value: $in,
        );
    }

    public static function notIn(string $column, array $in): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::NOT_IN,
            value: $in,
        );
    }

    public static function greaterThan(string $column, int|string|float $than): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::GREATER_THAN,
            value: $than,
        );
    }

    public static function lessThan(string $column, int|string|float $than): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::LESS_THAN,
            value: $than
        );
    }

    public static function greaterThanOrEqual(string $column, int|string|float $than): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::GREATER_THAN_OR_EQUAL,
            value: $than,
        );
    }

    public static function lessThanOrEqual(string $column, int|string|float $than): LoaderCondition
    {
        return new LoaderCondition(
            column: $column,
            operator: WhereCondition::LESS_THAN_OR_EQUAL,
            value: $than
        );
    }

    public static function raw(string $condition, array $parameters): LoaderConditionRaw
    {
        return new LoaderConditionRaw(
            condition: $condition,
            parameters: $parameters,
        );
    }


    public static function or(LoaderCondition $condition): LoaderCondition
    {
        return new LoaderCondition(
            column: $condition->column,
            operator: $condition->operator,
            value: $condition->value,
            type: WhereType::OR,
        );
    }

    public static function like(string $field, mixed $value): LoaderCondition
    {
        return new LoaderCondition(
            column: $field,
            operator: WhereCondition::LIKE,
            value: $value,
        );
    }

    public static function ColumnsEqual(string $column1, string $column2): LoaderCondition
    {
        return new LoaderCondition(
            column: $column1,
            operator: WhereCondition::EQUALS,
            value: $column2,
            parameteriseValue: false,
        );

    }

    public function applyToQuery(driver $query): driver
    {
        $method = $this->type->toCallable($query);

        $method(
            '(' . $this->column . ')',
            $this->parameteriseValue ? $this->value : Raw::create($this->value),
            $this->operator
        );

        return $query;
    }

    public function shouldWrap(): bool
    {
        if ($this->operator === WhereCondition::BETWEEN && !$this->value) {
            return false;
        }

        return $this->shouldWrap;
    }
}

