<?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->isAttributeColumn($column)) {
            $left = $this->getModelAttributeValue($model, substr($column, strlen('attribute.')));
            return $this->compareConditionValues($left, $operator, $value);
        }

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

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

    protected function isAttributeColumn(?string $column): bool
    {
        return $column !== null && str_starts_with($column, 'attribute.');
    }

    /**
     * Resolve a custom attribute value from the loaded model, joining multiple values with a comma.
     *
     * @param mixed $model
     * @param string $slug
     * @return mixed
     */
    protected function getModelAttributeValue(mixed $model, string $slug): mixed
    {
        if (!method_exists($model, 'attributeValues')) {
            return null;
        }

        $values = $model->attributeValues
            ->where('slug', $slug)
            ->map(fn ($value) => $value->getValue())
            ->filter(fn ($value) => $value !== null && $value !== '')
            ->values();

        if ($values->isEmpty()) {
            return null;
        }

        return $values->count() === 1 ? $values->first() : $values->implode(',');
    }

    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,
        };
    }
}
