<?php

app_require('routes/moduleWeatherAPIRoute.php');

use routes\moduleWeatherAPIRoute;

function department_weather_fallback_invoke_private(moduleWeatherAPIRoute $route, string $method, array $args = []): mixed
{
    $reflection = new ReflectionClass($route);
    $target = $reflection->getMethod($method);

    return $target->invokeArgs($route, $args);
}

function department_weather_coordinate_property(mixed $value): object
{
    return new class($value) {
        public function __construct(private mixed $value)
        {
        }

        public function value(): mixed
        {
            return $this->value;
        }
    };
}

function department_weather_fake_department(mixed $latitude, mixed $longitude): object
{
    return (object)[
        'latitude' => department_weather_coordinate_property($latitude),
        'longitude' => department_weather_coordinate_property($longitude),
    ];
}

beforeEach(function (): void {
    $_SERVER['REQUEST_URI'] = '/departments/weather';
});

it('resolves weather coordinates from valid coordinates only', function (): void {
    $route = new moduleWeatherAPIRoute();
    $coordinates = department_weather_fallback_invoke_private($route, 'resolveWeatherCoordinates', [[
        department_weather_fake_department('', ''),
        department_weather_fake_department('0', '0'),
        department_weather_fake_department('91.0', '12.0'),
        department_weather_fake_department('55.6761', '181.0'),
        department_weather_fake_department('55.6761', '12.5683'),
        department_weather_fake_department(56.2639, 9.5018),
    ]]);

    expect($coordinates)->toBeArray();
    expect(round($coordinates['lat'], 4))->toBe(round((55.6761 + 56.2639) / 2, 4));
    expect(round($coordinates['lon'], 4))->toBe(round((12.5683 + 9.5018) / 2, 4));
});

it('returns null weather coordinates when all department locations are invalid', function (): void {
    $route = new moduleWeatherAPIRoute();
    $coordinates = department_weather_fallback_invoke_private($route, 'resolveWeatherCoordinates', [[
        department_weather_fake_department('', ''),
        department_weather_fake_department('0', '0'),
        department_weather_fake_department('95.0', '12.0'),
        department_weather_fake_department('55.0', '-181.0'),
    ]]);

    expect($coordinates)->toBeNull();
});

it('classifies weatherapi no matching location errors as location lookup failures', function (): void {
    $route = new moduleWeatherAPIRoute();

    $is_location_error = department_weather_fallback_invoke_private($route, 'isWeatherLocationLookupFailure', [
        new Exception('WeatherAPI request failed with HTTP 400: No matching location found.'),
    ]);

    expect($is_location_error)->toBeTrue();
});

it('does not classify non-location weatherapi errors as location lookup failures', function (): void {
    $route = new moduleWeatherAPIRoute();

    $is_location_error = department_weather_fallback_invoke_private($route, 'isWeatherLocationLookupFailure', [
        new Exception('WeatherAPI request failed with HTTP 401: API key is invalid.'),
    ]);

    expect($is_location_error)->toBeFalse();
});

it('returns an empty forecast fallback when coordinates are unavailable', function (): void {
    $route = new moduleWeatherAPIRoute();
    $result = department_weather_fallback_invoke_private($route, 'fetchDepartmentForecastOrFallback', [null, 2]);

    expect($result['fallback_reason'])->toBe('invalid_department_coordinates');
    expect($result['forecast'])->toBeObject();
    expect($result['forecast']->forecast->forecastday ?? null)->toBeArray();
    expect($result['forecast']->forecast->forecastday ?? [1])->toHaveCount(0);
});

it('builds timeline entries with mostly_clear weather when forecast payload is empty', function (): void {
    $route = new moduleWeatherAPIRoute();
    $start = new DateTime('2026-03-24 08:00:00');
    $end_exclusive = new DateTime('2026-03-24 11:00:00');

    $timeline = department_weather_fallback_invoke_private($route, 'buildDepartmentWeatherTimeline', [
        [],
        [],
        (object)['forecast' => (object)['forecastday' => []]],
        [
            'start' => $start,
            'endExclusive' => $end_exclusive,
        ],
    ]);

    expect($timeline)->toHaveCount(3);
    expect($timeline[0]['date'])->toBe('2026-03-24');
    expect($timeline[0]['time'])->toBe('08:00');
    expect($timeline[0]['weather'])->toBe('mostly_clear');
    expect($timeline[0]['washes'])->toBe(0);
    expect($timeline[0]['hours'])->toBe(0.0);
    expect($timeline[0]['status'])->toBe('unknown');
    expect($timeline[1]['weather'])->toBe('mostly_clear');
    expect($timeline[2]['weather'])->toBe('mostly_clear');
});

it('wires departments weather route through the forecast fallback path', function (): void {
    $routeFile = app_path('routes/moduleWeatherAPIRoute.php');
    $content = (string)file_get_contents($routeFile);

    expect($content)->toContain('fetchDepartmentForecastOrFallback');
    expect($content)->toContain('$this->resolveForecastDaysForTimelineRange');
    expect($content)->toContain('$this->buildDepartmentWeatherTimeline');
    expect($content)->not->toContain('static function () use ($coordinates');
    expect($content)->toContain('DEPARTMENTS_WEATHER_FALLBACK');
    expect($content)->not->toContain('Selected department(s) do not have GPS coordinates configured');
});
