<?php

namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\CatalogOffer;
use Mtc\MercuryDataModels\CatalogOfferRule;

class CatalogOfferMatcher
{
    public function __construct(
        private Builder $query
    ) {
        //
    }

    public function getMatchingOffers(Collection $offers): Collection
    {
        return $offers->filter(fn(CatalogOffer $offer) => $this->matches($offer));
    }

    public function matches(CatalogOffer $offer): bool
    {
        if ($offer->rules->isEmpty()) {
            return true;
        }

        $query = clone $this->query;

        foreach ($offer->rules as $rule) {
            $this->applyRule($query, $rule);
        }

        return $query->exists();
    }

    private function applyRule(Builder $query, CatalogOfferRule $rule): void
    {
        $value = $this->parseValue($rule->value);

        match ($rule->condition) {
            '=' => $query->where($rule->field, '=', $value),
            '!=' => $query->where($rule->field, '!=', $value),
            '>' => $query->where($rule->field, '>', $value),
            '>=' => $query->where($rule->field, '>=', $value),
            '<' => $query->where($rule->field, '<', $value),
            '<=' => $query->where($rule->field, '<=', $value),
            'is_null' => $query->whereNull($rule->field),
            'exists' => $query->whereNotNull($rule->field),
            'in' => $query->whereIn($rule->field, (array) $value),
            'not_in' => $query->whereNotIn($rule->field, (array) $value),
            default => null,
        };
    }

    private function parseValue(mixed $value): mixed
    {
        if (is_string($value) && str_starts_with($value, '[')) {
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return $decoded;
            }
        }

        return $value;
    }
}
