<?php

namespace App;

use App\Http\Resources\VehicleList;
use App\Http\Resources\VehiclesCompareResource;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\Services\LocatingService;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;

class VehicleRepository
{
    public function topMakes(?int $limit): Collection
    {
        $vehicles = Vehicle::query()
            ->active()
            ->groupBy('make_id')
            ->whereNotNull('make_id')
            ->addSelect([
                'make_id',
                DB::raw('MIN(price) as cheapest_price'),
                DB::raw('count(id) as vehicle_count')
            ])
            ->orderByDesc('vehicle_count')
            ->take($limit ?? 8)
            ->get()
            ->keyBy('make_id');

        return VehicleMake::query()
            ->whereIn('id', $vehicles->pluck('make_id'))
            ->get()
            ->map(fn(VehicleMake $make) => [
                'name' => $make->name,
                'count' => $vehicles[$make->id]?->vehicle_count,
                'from_price' => $vehicles[$make->id]?->cheapest_price,
                'slug' => $make->slug,
                'logo' => $make->logo,
            ])
            ->sortByDesc(fn($item) => $item['count'])
            ->values();
    }

    public function topBodyTypes(?int $limit): Collection
    {
        $vehicles = Vehicle::query()
            ->active()
            ->groupBy('body_style_id')
            ->whereNotNull('body_style_id')
            ->addSelect([
                'body_style_id',
                DB::raw('MIN(price) as cheapest_price'),
                DB::raw('count(id) as vehicle_count')
            ])
            ->orderByDesc('vehicle_count')
            ->take($limit ?? 8)
            ->get()
            ->keyBy('body_style_id');

        return BodyStyleType::query()
            ->whereIn('id', $vehicles->pluck('body_style_id'))
            ->get()
            ->map(fn(BodyStyleType $type) => [
                'name' => $type->name,
                'count' => $vehicles[$type->id]?->vehicle_count,
                'from_price' => $vehicles[$type->id]?->cheapest_price,
                'slug' => $type->slug,
            ])
            ->sortByDesc(fn($item) => $item['count'])
            ->values();
    }

    public function topFuelTypes(?int $limit): Collection
    {
        $vehicles = Vehicle::query()
            ->active()
            ->groupBy('fuel_type_id')
            ->whereNotNull('fuel_type_id')
            ->addSelect([
                'fuel_type_id',
                DB::raw('MIN(price) as cheapest_price'),
                DB::raw('count(id) as vehicle_count')
            ])
            ->orderByDesc('vehicle_count')
            ->take($limit ?? 8)
            ->get()
            ->keyBy('fuel_type_id');

        return FuelType::query()
            ->whereIn('id', $vehicles->pluck('fuel_type_id'))
            ->get()
            ->map(fn(FuelType $type) => [
                'name' => $type->name,
                'count' => $vehicles[$type->id]?->vehicle_count,
                'from_price' => $vehicles[$type->id]?->cheapest_price,
                'slug' => $type->slug,
            ])
            ->sortByDesc(fn($item) => $item['count'])
            ->values();
    }

    public function similar(Vehicle $vehicle, int $limit = 4, ?string $matchType = 'price'): VehicleList
    {
        return new VehicleList(
            Vehicle::query()
                ->active()
                ->withRelationshipsForCardView()
                ->similar($matchType ?? 'price', $vehicle)
                ->where('id', '!=', $vehicle->id)
                ->take($limit * 2)
                ->get()
                ->shuffle()
                ->take($limit)
        );
    }

    public function featured(?Vehicle $vehicle, int $limit = 4, ?array $filters = []): VehicleList
    {
        $query = Vehicle::query()
            ->where('featured', 1)
            ->active()
            ->withRelationshipsForCardView()
            ->withImageCount()
            ->when($vehicle, fn(Builder $query) => $query->where('id', 'not like', $vehicle?->id))
            ->inRandomOrder()
            ->take($limit);

        $this->applyFilters($query, $filters);

        return new VehicleList($query->get());
    }

    public function recentlyViewed(?Vehicle $vehicle, array $vehicleSlugs = [], int $limit = 4): VehicleList
    {
        return new VehicleList(
            Vehicle::query()
                ->whereIn('slug', $vehicleSlugs)
                ->active()
                ->withRelationshipsForCardView()
                ->withImageCount()
                ->when($vehicle, fn(Builder $query) => $query->where('id', 'not like', $vehicle?->id))
                ->inRandomOrder()
                ->take($limit)
                ->get()
        );
    }

    public function compare(array $vehicleSlugs = []): VehiclesCompareResource
    {
        return new VehiclesCompareResource(
            Vehicle::query()
                ->whereIn('slug', $vehicleSlugs)
                ->active()
                ->with(['features', 'specs'])
                ->withRelationshipsForCardView()
                ->get()
        );
    }

    public function recentlyAdded(int $limit = 4, array $filters = []): VehicleList
    {
        $query = Vehicle::query()
            ->active()
            ->withRelationshipsForCardView()
            ->withImageCount()
            ->latest()
            ->take($limit);

        $this->applyFilters($query, $filters);

        return new VehicleList($query->get());
    }

    public function trackView(Vehicle $vehicle): void
    {
        $view = $vehicle->views()
            ->firstOrNew(['date' => Carbon::now()->format('Y-m-d')]);
        $view->hits++;
        $view->save();
    }

    public function trackQuickView(Vehicle $vehicle): void
    {
        $view = $vehicle->views()
            ->firstOrNew(['date' => Carbon::now()->format('Y-m-d')]);
        $view->quick_view_hits++;
        $view->save();
    }

    private function applyFilters(&$query, $filters): void
    {
        $query
            ->when(
                !empty($filters['min_image_count']),
                fn(Builder $query) => $query->having('image_count', '>', (int)$filters['min_image_count'])
            )
            ->when(
                $filters['make'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'make',
                    fn(Builder $indexQuery) => is_array($filters['make'])
                        ? $indexQuery->whereIn('slug', $filters['make'])
                        : $indexQuery->where('slug', $filters['make'])
                )
            )
            ->when(
                $filters['model'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'model',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['model'])
                )
            )
            ->when(
                $filters['fuel_type'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'fuelType',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['fuel_type'])
                )
            )
            ->when(
                $filters['franchise'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'dealership.franchise',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['franchise'])
                )
            )
            ->when(
                $filters['location'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'dealership',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['location'])
                )
            )
            ->when(
                $filters['type'] ?? null,
                fn(Builder $query) => $query->where(
                    'type',
                    $filters['type']
                )
            )
            ->when(
                $filters['is_new'] ?? null,
                fn(Builder $query) => $query->where(
                    'is_new',
                    $filters['is_new']
                )
            )
            ->when(
                !empty($filters['postcode']),
                function (Builder $query) use ($filters) {
                    try {
                        $distance = !empty($filters['radius']) ? (int)$filters['radius'] : 200;
                        $locationService = app(LocatingService::class);
                        $location = $locationService->locate($filters['postcode']);

                        if (!empty($location)) {
                            $query->isWithinDistance($location->lat(), $location->lng(), $distance);
                        }
                    } catch (\Exception $exception) {
                        Log::debug("Error locating postcode {$filters['postcode']}: " . $exception->getMessage());
                    }
                }
            );
    }
}
