<?php

namespace App\Services;

use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleReview;

class VehicleContentService
{
    private const DEFAULT_PER_PAGE = 15;

    private const CAR_AND_DRIVING_PROVIDER = 'car-and-driving';

    public function getVideos(
        ?string $make = null,
        ?string $model = null,
        ?int $perPage = null,
        int $page = 1,
        bool $singleImagePlaceholder = false
    ): LengthAwarePaginator {
        $perPage = $perPage ?? self::DEFAULT_PER_PAGE;

        $franchiseMakes = $this->getFranchiseMakeNames();

        // Stock vehicles with a video URL
        $stockVideos = Vehicle::query()
            ->active()
            ->whereNotNull('main_video_url')
            ->where('main_video_url', '!=', '')
            ->with(['make', 'model', 'primaryMediaUse.media'])
            ->when($singleImagePlaceholder, fn ($q) => $q->withImageCount())
            ->when($make, fn ($q) => $q->whereHas('make', fn ($mq) => $mq->whereRaw('LOWER(name) = ?', [strtolower($make)])))
            ->when($model, fn ($q) => $q->whereHas('model', fn ($mq) => $mq->whereRaw('LOWER(name) = ?', [strtolower($model)])))
            ->get()
            ->groupBy(fn (Vehicle $v) => $v->make_id . '-' . $v->model_id)
            ->map(fn (Collection $group) => $group->first())
            ->map(fn (Vehicle $vehicle) => $this->mapVideoData($vehicle, $singleImagePlaceholder));

        // Non-stock C&D reviews that have a stored video URL and no active linked vehicle
        $nonStockVideos = VehicleReview::query()
            ->where('provider', self::CAR_AND_DRIVING_PROVIDER)
            ->whereNotNull('main_video_url')
            ->when($make, fn ($q) => $q->whereRaw('LOWER(make) = ?', [strtolower($make)]))
            ->when($model, fn ($q) => $q->whereRaw('LOWER(model) = ?', [strtolower($model)]))
            ->whereDoesntHave('vehicles', fn ($q) => $q->active())
            ->get()
            ->unique(fn (VehicleReview $r) => strtolower($r->make . '-' . $r->model))
            ->map(fn (VehicleReview $r) => $this->mapNonStockVideoData($r, $franchiseMakes));

        $merged = $stockVideos->values()
            ->concat($nonStockVideos->values())
            ->sortByDesc(fn ($item) => $item['is_franchise'] ? 1 : 0)
            ->values();

        return $this->paginateCollection($merged, $perPage, $page);
    }

    public function getReviews(
        string $provider,
        ?string $make = null,
        ?string $model = null,
        ?int $perPage = null,
        int $page = 1
    ): LengthAwarePaginator {
        $perPage = $perPage ?? self::DEFAULT_PER_PAGE;

        $franchiseMakes = $this->getFranchiseMakeNames();

        $reviews = VehicleReview::query()
            ->where('provider', $provider)
            ->whereNotNull('review')
            ->when($make, fn ($q) => $q->whereRaw('LOWER(make) = ?', [strtolower($make)]))
            ->when($model, fn ($q) => $q->whereRaw('LOWER(model) = ?', [strtolower($model)]))
            ->with(['vehicles' => function ($q) use ($make, $model) {
                $q->active()
                    ->with(['make', 'model', 'primaryMediaUse.media'])
                    ->when($make, fn ($vq) => $vq->whereHas('make', fn ($mq) => $mq->whereRaw('LOWER(name) = ?', [strtolower($make)])))
                    ->when($model, fn ($vq) => $vq->whereHas('model', fn ($mq) => $mq->whereRaw('LOWER(name) = ?', [strtolower($model)])));
            }])
            ->get();

        $filtered = $reviews
            ->map(fn (VehicleReview $review) => $this->mapReviewData($review, $franchiseMakes))
            ->unique('make_model_key')
            ->sortByDesc(fn ($item) => $item['is_franchise'] ? 1 : 0)
            ->values();

        return $this->paginateCollection($filtered, $perPage, $page);
    }

    public function getMakes(string $provider): array
    {
        return VehicleReview::query()
            ->where('provider', $provider)
            ->whereNotNull('make')
            ->where('make', '!=', '')
            ->distinct()
            ->orderBy('make')
            ->pluck('make')
            ->toArray();
    }

    public function getModels(string $provider, ?string $make = null): array
    {
        return VehicleReview::query()
            ->where('provider', $provider)
            ->whereNotNull('model')
            ->where('model', '!=', '')
            ->when($make, fn ($q) => $q->whereRaw('LOWER(make) = ?', [strtolower($make)]))
            ->distinct()
            ->orderBy('model')
            ->pluck('model')
            ->toArray();
    }

    private function paginateCollection(Collection $collection, int $perPage, int $page): LengthAwarePaginator
    {
        $total = $collection->count();
        $items = $collection->slice(($page - 1) * $perPage, $perPage)->values();

        return new LengthAwarePaginator(
            $items,
            $total,
            $perPage,
            $page,
            ['path' => request()->url(), 'query' => request()->query()]
        );
    }

    private function mapVideoData(Vehicle $vehicle, bool $singleImagePlaceholder = false): array
    {
        $thumbnail = $vehicle->getPreviewImage('medium');

        if ($singleImagePlaceholder && $vehicle->image_count <= 1) {
            $thumbnail = null;
        }

        return [
            'video_url'      => $vehicle->main_video_url,
            'slug'           => '/vehicles/' . $vehicle->slug,
            'title'          => $vehicle->title,
            'thumbnail'      => $thumbnail,
            'make_model_key' => $vehicle->make_id . '-' . $vehicle->model_id,
            'is_franchise'   => true,
        ];
    }

    private function mapNonStockVideoData(VehicleReview $review, array $franchiseMakes): array
    {
        return [
            'video_url'      => $review->main_video_url,
            'slug'           => null,
            'title'          => trim($review->make . ' ' . $review->model),
            'thumbnail'      => null,
            'make_model_key' => $review->make . '-' . $review->model,
            'is_franchise'   => in_array(strtolower((string) $review->make), $franchiseMakes, true),
        ];
    }

    private function mapReviewData(VehicleReview $review, array $franchiseMakes): array
    {
        $vehicle = $review->vehicles->first();

        if ($vehicle) {
            return [
                'review'         => $review->review,
                'slug'           => '/vehicles/' . $vehicle->slug,
                'title'          => $vehicle->title,
                'thumbnail'      => $vehicle->getPreviewImage('medium'),
                'make_model_key' => $vehicle->make_id . '-' . $vehicle->model_id,
                'is_franchise'   => true,
            ];
        }

        return [
            'review'         => $review->review,
            'slug'           => null,
            'title'          => trim($review->make . ' ' . $review->model),
            'thumbnail'      => null,
            'make_model_key' => $review->make . '-' . $review->model,
            'is_franchise'   => in_array(strtolower((string) $review->make), $franchiseMakes, true),
        ];
    }

    /**
     * Returns the lowercased names of every make with at least one active vehicle in stock.
     * Used to flag non-stock content cards as franchise so they sort to the top of the listing.
     */
    private function getFranchiseMakeNames(): array
    {
        return VehicleMake::query()
            ->whereHas('vehicles', fn ($q) => $q->active())
            ->pluck('name')
            ->map(fn (string $name) => strtolower($name))
            ->all();
    }
}
