<?php

namespace App\Services;

use App\Facades\Settings;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\Dealership;

/**
 * Resolves which co-located dealership an AutoTrader-imported vehicle belongs to.
 *
 * A single AutoTrader advertiser (location id) can front several co-located dealerships
 * (e.g. Ford, BMW and Honda at one rooftop). Those siblings are grouped by their shared
 * `auto-trader-location-id`. A vehicle is assigned to the sibling whose franchise matches
 * the vehicle make; anything unmatched or ambiguous falls back to the group's catchall.
 */
class AutoTraderDealershipResolver
{
    /**
     * Memoised co-located groups, keyed by location id.
     *
     * @var array<string, Collection<int, Dealership>>
     */
    private array $groupCache = [];

    /**
     * Resolve the dealership for a vehicle at the given AutoTrader location.
     *
     * @param  string|int  $locationId  the shared auto-trader-location-id
     * @param  int|null  $makeId  the vehicle's make_id
     */
    public function resolve(string|int $locationId, ?int $makeId): ?Dealership
    {
        $group = $this->group((string) $locationId);

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

        if ($makeId !== null) {
            $matched = $group->filter(fn (Dealership $dealership) => $this->matchesMake($dealership, $makeId));

            if ($matched->count() === 1) {
                return $matched->first();
            }
        }

        return $this->catchall($group);
    }

    /**
     * All dealerships sharing the given AutoTrader location id, ordered by id so that the
     * catchall fallback is deterministic.
     *
     * @return Collection<int, Dealership>
     */
    private function group(string $locationId): Collection
    {
        return $this->groupCache[$locationId] ??= Dealership::query()
            ->with(['franchise', 'franchises'])
            ->orderBy('id')
            ->get()
            ->filter(function (Dealership $dealership) use ($locationId) {
                return (string) ($dealership->data['auto-trader-location-id'] ?? '') === $locationId;
            })
            ->values();
    }

    /**
     * Whether the dealership has a franchise for the given make, honouring the
     * single-vs-multi franchise setting exactly as Franchise::dealerships() does.
     */
    private function matchesMake(Dealership $dealership, int $makeId): bool
    {
        if (Settings::get('dealerships-multiple-franchises')) {
            return $dealership->franchises->contains('make_id', $makeId);
        }

        // Loose comparison: foreign-key columns are not cast, so make_id may be a string.
        return optional($dealership->franchise)->make_id == $makeId;
    }

    /**
     * The group's catchall dealership: the flagged one, or the lowest-id dealership as a
     * deterministic last resort when none is flagged.
     *
     * @param  Collection<int, Dealership>  $group
     */
    private function catchall(Collection $group): Dealership
    {
        return $group->firstWhere('is_autotrader_catchall', true) ?? $group->first();
    }
}
