<?php

namespace App\Traits;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;

trait AppliesModelConditions
{
    protected function applyModelConditions(mixed $model, array|Collection $conditions): bool
    {
        return collect($conditions)
            ->map(fn ($c) => $c['value'])
            ->every(fn ($condition) => $this->applyModelCondition($model, $condition));
    }

    protected function applyModelCondition(mixed $model, array $condition): bool
    {
        $column = $condition['column'] ?? null;
        $operator = $condition['operator'] ?? '=';
        $value = $condition['value'] ?? null;

        if ($this->isRelationshipCountCondition($column)) {
            return $this->applyRelationshipCountCondition($model, $column, $operator, $value);
        }

        return $this->compareConditionValues(data_get($model, $column), $operator, $value);
    }

    protected function applyRelationshipCountCondition(mixed $model, string $column, string $operator, $value): bool
    {
        $relationship = Str::beforeLast($column, '.count');
        $related = data_get($model, $relationship);

        return is_iterable($related) && $this->compareConditionValues(count($related), $operator, $value);
    }

    protected function isRelationshipCountCondition(string $column): bool
    {
        return Str::endsWith($column, '.count');
    }

    protected function compareConditionValues(mixed $left, string $operator, mixed $right): bool
    {
        return match ($operator) {
            '='  => $left == $right,
            '!=' => $left != $right,
            '<'  => (float)$left < (float)$right,
            '<=' => (float)$left <= (float)$right,
            '>'  => (float)$left > (float)$right,
            '>=' => (float)$left >= (float)$right,
            'like', '%like%' => Str::contains((string)$left, str_replace('%', '', (string)$right)),
            'like%' => Str::startsWith((string)$left, str_replace('%', '', (string)$right)),
            'in' => in_array($left, (array) $right, true),
            default => false,
        };
    }
}
