<?php

namespace App\Services;

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

class DealershipDetection
{
    /**
     * Determines the dealership based on the provided reason data.
     *
     * @param ?string $fallbackDealerId The fallback dealer ID if no other dealer is found.
     * @param ?int $dealer_id The dealer ID to use directly.
     * @param ?string $postcode The postcode to find the closest dealership.
     *
     * @return ?Dealership The determined dealership or null if not found.
     */
    public function determineDealer(
        ?string $fallbackDealerId,
        ?int $dealer_id = null,
        ?string $postcode = null,
    ): ?Dealership {
        $dealerId = $dealer_id
            ?? ($postcode ? $this->getClosestDealershipToPostcode($postcode)?->id : null)
            ?? $fallbackDealerId;

        return $dealerId ? Dealership::find($dealerId) : null;
    }

    /**
     * Find the closest dealership to a postcode.
     */
    public function getClosestDealershipToPostcode(string $postcode): ?Dealership
    {
        $dealerships = $this->getAllDealerships();
        $locatingService = App::make(LocatingService::class);

        try {
            $postcodeLatLong = $locatingService->locate($postcode);
        } catch (\Exception) {
            return null;
        }

        if (!$postcodeLatLong || (!$postcodeLatLong->lat() && !$postcodeLatLong->lng())) {
            return null;
        }

        $closestDealerId = $dealerships
            ->filter(fn ($dealer) => str_contains((string) $dealer->coordinates, ','))
            ->mapWithKeys(fn ($dealer) => [
                $dealer->id => $locatingService->getDistanceBetween(
                    $postcodeLatLong->lat(),
                    $postcodeLatLong->lng(),
                    ...explode(',', (string) $dealer->coordinates)
                )
            ])
            ->sort()
            ->keys()
            ->first();

        return $closestDealerId ? Dealership::find($closestDealerId) : null;
    }

    /**
     * Get all dealerships.
     */
    private function getAllDealerships(): Collection
    {
        return Dealership::all();
    }
}
