<?php

use App\Livewire\Public\Fuel\Search;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Livewire\Livewire;

uses(RefreshDatabase::class);

it('renders the search component', function () {
    Livewire::test(Search::class)
        ->assertStatus(200)
        ->assertSeeHtml('name="search"');
});

it('has default property values', function () {
    Livewire::test(Search::class)
        ->assertSet('search', '')
        ->assertSet('fuelType', 'petrol')
        ->assertSet('radius', 5)
        ->assertSet('sort', 'reliable')
        ->assertSet('apiError', null)
        ->assertSet('hasSearched', false);
});

it('validates search is required', function () {
    Livewire::test(Search::class)
        ->call('findStations')
        ->assertHasErrors(['search' => 'required']);
});

it('validates fuelType is required', function () {
    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', '')
        ->call('findStations')
        ->assertHasErrors(['fuelType' => 'required']);
});

it('dispatches stations-found with results, meta, prediction and radius on successful search', function () {
    Http::fake([
        '*/api/stations*' => Http::response([
            'data' => [
                [
                    'station_id' => 'abc123',
                    'name' => 'BP Garage',
                    'brand' => 'BP',
                    'is_supermarket' => false,
                    'address' => '1 High Street',
                    'postcode' => 'SW1A 1AA',
                    'lat' => 51.5074,
                    'lng' => -0.1278,
                    'distance_km' => 1.5,
                    'fuel_type' => 'e10',
                    'price_pence' => 14390,
                    'price' => 143.9,
                    'price_updated_at' => '2026-04-05T08:00:00.000Z',
                    'price_classification' => 'current',
                    'price_classification_label' => 'Current',
                ],
            ],
            'meta' => ['count' => 1, 'lowest_pence' => 14390, 'avg_pence' => 14390.0],
        ], 200),
        '*/api/prediction*' => Http::response([
            'action' => 'fill_now',
            'confidence_score' => 80.0,
            'confidence_label' => 'high',
            'reasoning' => 'Prices rising.',
            'predicted_direction' => 'up',
            'predicted_change_pence' => 3.5,
        ], 200),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'petrol')
        ->call('findStations')
        ->assertSet('hasSearched', true)
        ->assertSet('apiError', null)
        ->assertDispatched('stations-found', fn ($event, $params) =>
            count($params['results']) === 1
            && $params['results'][0]['name'] === 'BP Garage'
            && $params['meta']['count'] === 1
            && $params['prediction']['action'] === 'fill_now'
            && $params['radius'] === 5
        );
});

it('sets apiError from 422 station response and does not dispatch stations-found', function () {
    Http::fake([
        '*/api/stations*' => Http::response([
            'errors' => ['postcode' => ['Postcode not found.']],
        ], 422),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'ZZ99 9ZZ')
        ->set('fuelType', 'petrol')
        ->call('findStations')
        ->assertSet('hasSearched', false)
        ->assertSet('apiError', 'Postcode not found.')
        ->assertNotDispatched('stations-found');
});

it('sets generic apiError on server error', function () {
    Http::fake([
        '*/api/stations*' => Http::response([], 500),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'petrol')
        ->call('findStations')
        ->assertSet('apiError', 'Unable to fetch stations. Please try again.');
});

it('converts radius from miles to km in the outgoing stations request', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'petrol')
        ->set('radius', 5)
        ->call('findStations');

    Http::assertSent(function ($request) {
        if (! str_contains($request->url(), 'api/stations')) {
            return false;
        }
        $data = $request->data();
        return isset($data['radius']) && abs((float) $data['radius'] - 8.05) < 0.01;
    });
});

it('resets apiError before each new search', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'petrol')
        ->set('apiError', 'Old error')
        ->call('findStations')
        ->assertSet('apiError', null);
});

it('does not call findStations on updatedFuelType if not yet searched', function () {
    Http::fake();

    Livewire::test(Search::class)
        ->set('fuelType', 'diesel');

    Http::assertNothingSent();
});

it('re-runs findStations on updatedFuelType when already searched', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
    ]);

    Livewire::test(Search::class)
        ->set('hasSearched', true)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'diesel');

    Http::assertSentCount(2);
});

it('re-runs findStations on updatedRadius when already searched', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
    ]);

    Livewire::test(Search::class)
        ->set('hasSearched', true)
        ->set('search', 'SW1A 1AA')
        ->set('radius', 10);

    Http::assertSentCount(2);
});

it('re-runs findStations on updatedSort when already searched', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
    ]);

    Livewire::test(Search::class)
        ->set('hasSearched', true)
        ->set('search', 'SW1A 1AA')
        ->set('sort', 'price');

    Http::assertSentCount(2);
});

it('prediction is null in stations-found payload when prediction api fails', function () {
    Http::fake([
        '*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
        '*/api/prediction*' => Http::response([], 500),
    ]);

    Livewire::test(Search::class)
        ->set('search', 'SW1A 1AA')
        ->set('fuelType', 'petrol')
        ->call('findStations')
        ->assertSet('hasSearched', true)
        ->assertDispatched('stations-found', fn ($event, $params) =>
            $params['prediction'] === null
        );
});
