<?php

namespace App\Services;

use App\Models\ListingFeature;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;

/**
 * İlan listesine dinamik filtre uygular. Tamamen veri-odaklıdır:
 * admin panelden eklenen herhangi bir `is_filterable` feature, ek kod
 * yazmadan otomatik olarak filtrelenebilir hale gelir.
 *
 * Query convention:
 *   ?category=villa
 *   &price_min=500000&price_max=3000000
 *   &area_min=200&area_max=600
 *   &bedrooms=4&bathrooms=2
 *   &sort=price_asc
 *   &features[pool]=1                         (boolean)
 *   &features[heating_type]=Natural Gas       (select; dizi de olabilir → IN)
 *   &features[property_view][]=Sea&...[]=City (multiselect; herhangi biri)
 *   &features[building_age][max]=5            (number; min/max)
 */
class ListingFilterService
{
    private ?Collection $filterableFeatures = null;

    public function apply(Builder $query, Request $request): Builder
    {
        // ---- Temel alanlar ----
        if ($request->filled('category')) {
            $query->whereHas('category', fn ($q) => $q->where('slug', $request->query('category')));
        }

        if ($request->filled('city')) {
            $query->where('city', $request->query('city'));
        }

        if ($request->filled('country')) {
            $query->where('country', $request->query('country'));
        }

        if ($request->boolean('featured')) {
            $query->where('is_featured', true);
        }

        if ($request->filled('search')) {
            $search = $request->query('search');
            $locale = app()->getLocale();
            $like = "%{$search}%";

            // title/short_description/description çevrilebilir (JSON) → aktif dil,
            // yoksa EN fallback üzerinden ara. city/district düz metin.
            // Not: MariaDB'de JSON_UNQUOTE çıktısı utf8mb3'tür; bare LIKE bağlantı
            // zorlamasıyla harf+aksan katlar. COLLATE EKLEME (charset çakışması
            // ERROR 1253 verir). Locale, SetLocale middleware'inde ['tr','en']
            // beyaz listesinden gelir.
            $query->where(function ($q) use ($like, $locale) {
                $q->whereRaw("COALESCE(JSON_UNQUOTE(JSON_EXTRACT(title, CONCAT('$.', ?))), JSON_UNQUOTE(JSON_EXTRACT(title, '$.en'))) LIKE ?", [$locale, $like])
                    ->orWhereRaw("COALESCE(JSON_UNQUOTE(JSON_EXTRACT(short_description, CONCAT('$.', ?))), JSON_UNQUOTE(JSON_EXTRACT(short_description, '$.en'))) LIKE ?", [$locale, $like])
                    ->orWhereRaw("COALESCE(JSON_UNQUOTE(JSON_EXTRACT(description, CONCAT('$.', ?))), JSON_UNQUOTE(JSON_EXTRACT(description, '$.en'))) LIKE ?", [$locale, $like])
                    ->orWhere('city', 'like', $like)
                    ->orWhere('district', 'like', $like);
            });
        }

        // ---- Fiyat & alan aralığı ----
        $this->numericRange($query, 'price', $request->query('price_min'), $request->query('price_max'));
        $this->numericRange($query, 'gross_area', $request->query('area_min'), $request->query('area_max'));

        // ---- Kısa yollar: oda / banyo (number feature) ----
        if ($request->filled('bedrooms')) {
            $this->applyNumberFeature($query, 'room_count', ['min' => $request->query('bedrooms')]);
        }
        if ($request->filled('bathrooms')) {
            $this->applyNumberFeature($query, 'bathroom_count', ['min' => $request->query('bathrooms')]);
        }

        // ---- Dinamik feature filtreleri ----
        $features = $request->input('features', []);
        if (is_array($features)) {
            foreach ($features as $key => $value) {
                $this->applyFeature($query, (string) $key, $value);
            }
        }

        return $query;
    }

    public function applySort(Builder $query, ?string $sort): Builder
    {
        return match ($sort) {
            // MariaDB NULL'ları ASC'de başa koyar; fiyatsız ilanlar sonda kalsın
            'price_asc' => $query->orderByRaw('price IS NULL, price asc'),
            'price_desc' => $query->orderBy('price', 'desc'),
            'area_desc' => $query->orderBy('gross_area', 'desc'),
            'popular' => $query->orderBy('view_count', 'desc'),
            default => $query->latest('published_at'),
        };
    }

    /* --------------------------- yardımcılar --------------------------- */

    private function numericRange(Builder $query, string $column, $min, $max): void
    {
        if (is_numeric($min)) {
            $query->where($column, '>=', $min);
        }
        if (is_numeric($max)) {
            $query->where($column, '<=', $max);
        }
    }

    private function applyFeature(Builder $query, string $key, $value): void
    {
        $feature = $this->filterableFeatures()->get($key);
        if (! $feature) {
            return; // tanımsız veya filtrelenemeyen feature → yok say
        }

        switch ($feature->input_type) {
            case 'number':
                $range = is_array($value) ? $value : ['min' => $value];
                $this->applyNumberFeature($query, $key, $range);
                break;

            case 'boolean':
                if (filter_var($value, FILTER_VALIDATE_BOOLEAN)) {
                    $query->whereHas('featureValues', fn ($q) => $q
                        ->where('feature_id', $feature->id)
                        ->where('value_boolean', true));
                }
                break;

            case 'multiselect':
                $values = array_filter(is_array($value) ? $value : [$value], fn ($v) => $v !== '' && $v !== null);
                if (! empty($values)) {
                    $query->whereHas('featureValues', function ($q) use ($feature, $values) {
                        $q->where('feature_id', $feature->id)->where(function ($inner) use ($values) {
                            foreach ($values as $v) {
                                // value_json bir JSON dizi; "içeriyor mu" kontrolü
                                // (MariaDB'de json_contains'e derlenir)
                                $inner->orWhereJsonContains('value_json', $v);
                            }
                        });
                    });
                }
                break;

            default: // select / text
                $values = array_filter(is_array($value) ? $value : [$value], fn ($v) => $v !== '' && $v !== null);
                if (! empty($values)) {
                    $query->whereHas('featureValues', fn ($q) => $q
                        ->where('feature_id', $feature->id)
                        ->whereIn('value_text', $values));
                }
                break;
        }
    }

    private function applyNumberFeature(Builder $query, string $key, array $range): void
    {
        $feature = $this->filterableFeatures()->get($key);
        if (! $feature) {
            return;
        }

        $min = $range['min'] ?? null;
        $max = $range['max'] ?? null;

        if (! is_numeric($min) && ! is_numeric($max)) {
            return;
        }

        $query->whereHas('featureValues', function ($q) use ($feature, $min, $max) {
            $q->where('feature_id', $feature->id);
            if (is_numeric($min)) {
                $q->where('value_number', '>=', $min);
            }
            if (is_numeric($max)) {
                $q->where('value_number', '<=', $max);
            }
        });
    }

    /** key => ListingFeature map'i (sadece aktif & filtrelenebilir). */
    private function filterableFeatures(): Collection
    {
        if ($this->filterableFeatures === null) {
            $this->filterableFeatures = ListingFeature::query()
                ->where('is_active', true)
                ->where('is_filterable', true)
                ->get()
                ->keyBy('key');
        }

        return $this->filterableFeatures;
    }
}
