<?php

namespace App\Services;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Support\Facades\DB;

class DistanceService
{
    private const EARTH_RADIUS_KM = 6371;

    public static function calculate(?float $lat1, ?float $lng1, ?float $lat2, ?float $lng2): ?float
    {
        if ($lat1 === null || $lng1 === null || $lat2 === null || $lng2 === null) {
            return null;
        }

        $lat1Rad = deg2rad($lat1);
        $lat2Rad = deg2rad($lat2);
        $lng1Rad = deg2rad($lng1);
        $lng2Rad = deg2rad($lng2);

        $cosValue = cos($lat1Rad) * cos($lat2Rad) * cos($lng2Rad - $lng1Rad)
            + sin($lat1Rad) * sin($lat2Rad);

        $cosValue = min(1.0, max(-1.0, $cosValue));

        return round(self::EARTH_RADIUS_KM * acos($cosValue), 1);
    }

    public static function sqlExpression(
        ?float $clientLatitude,
        ?float $clientLongitude,
        string $targetLatCol = 'provider_address.latitude',
        string $targetLngCol = 'provider_address.longitude',
        string $alias        = 'distance_km'
    ): Expression {
        if ($clientLatitude === null || $clientLongitude === null) {
            return DB::raw("NULL as {$alias}");
        }

        return DB::raw("
            CASE
                WHEN {$targetLatCol} IS NOT NULL
                AND {$targetLngCol} IS NOT NULL
                THEN ROUND(
                    (
                        " . self::EARTH_RADIUS_KM . " * ACOS(
                            LEAST(
                                1,
                                GREATEST(
                                    -1,
                                    COS(RADIANS({$clientLatitude}))
                                        * COS(RADIANS({$targetLatCol}))
                                        * COS(
                                            RADIANS({$targetLngCol})
                                            - RADIANS({$clientLongitude})
                                        )
                                        + SIN(RADIANS({$clientLatitude}))
                                        * SIN(RADIANS({$targetLatCol}))
                                )
                            )
                        )
                    )::numeric,
                    1
                )
                ELSE NULL
            END AS {$alias}
        ");
    }
}
