<?php

use App\Enums\AcarsType;
use App\Enums\FareType;
use App\Enums\UserState;
use App\Models\Acars;
use App\Models\Aircraft;
use App\Models\Airport;
use App\Models\Flight;
use App\Models\Pirep;
use App\Models\Rank;
use App\Models\SimBrief;
use App\Models\Subfleet;
use App\Models\User;
use App\Services\SimBriefService;
use App\Support\Utils;
use Carbon\Carbon;

/**
 * @param array $attrs Additional user attributes
 *
 * @throws Exception
 */
function createUserData(array $attrs = []): array
{
    $subfleet = Subfleet::factory()->hasAircraft(2)->create();
    $rank = Rank::factory()->hasAttached($subfleet)->create();

    /** @var User $user */
    $user = User::factory()->create(array_merge([
        'flight_time' => 1000,
        'rank_id'     => $rank->id,
        'state'       => UserState::ACTIVE,
    ], $attrs));

    return [
        'subfleet' => $subfleet,
        'aircraft' => $subfleet->aircraft,
        'user'     => $user,
    ];
}

/**
 * Load SimBrief
 */
function loadSimBrief(User $user, Aircraft $aircraft, array $fares = [], ?string $flight_id = null): SimBrief
{
    if (in_array($flight_id, [null, '', '0'], true)) {
        $flight_id = 'FLIGHT_ID_1';
    }

    /** @var Flight $flight */
    $flight = Flight::factory()->create([
        'id'             => $flight_id,
        'dpt_airport_id' => 'OMAA',
        'arr_airport_id' => 'OMDB',
    ]);

    return downloadOfp($user, $flight, $aircraft, $fares);
}

/**
 * Download an OFP file
 */
function downloadOfp(User $user, $flight, $aircraft, array $fares): ?SimBrief
{

    Illuminate\Support\Facades\Http::fake([
        'simbrief.com/*' => Http::response(readDataFile('simbrief/briefing.json'), 200, ['Content-Type' => 'application/json']),
    ]);

    return app(SimBriefService::class)->downloadOfp($user, 'static_id', Utils::generateNewId(), $flight->id, $aircraft->id, $fares);
}

test('read simbrief', function (): void {
    $userinfo = createUserData();
    $user = $userinfo['user'];
    $briefing = loadSimBrief($user, $userinfo['aircraft']->first(), []);

    expect($briefing->ofp_json_path)->not->toBeEmpty()
        ->and($briefing->ofp)->not->toBeNull();

    // Spot check reading of the files
    $files = $briefing->files;
    expect($files->count())->toEqual(67)
        ->and($files->firstWhere('name',
            'PDF Document')['url'])->toEqual('https://www.simbrief.com/ofp/flightplans/OMAAOMDB_PDF_1584226092.pdf');

    // Spot check reading of images
    $images = $briefing->images;
    expect($images->count())->toEqual(6)
        ->and($images->firstWhere('name',
            'Route')['url'])->toEqual('https://www.simbrief.com/ofp/uads/OMAAOMDB_UAD_1584226092_ROUTE.gif');

    $level = $briefing->ofp->general->initial_altitude;
    expect($level)->toEqual(9000);

    // Read the flight route
    $routeStr = $briefing->ofp->general->route;
    expect($routeStr)->toEqual('DCT BOMUP DCT LOVIM DCT RESIG DCT NODVI DCT OBMUK DCT LORID DCT '.
    'ORGUR DCT PEBUS DCT EMOPO DCT LOTUK DCT LAGTA DCT LOVOL');
});

test('api calls', function (): void {
    $userinfo = createUserData();
    $user = $userinfo['user'];

    apiAs($user);

    $aircraft = $userinfo['aircraft']->random();
    $briefing = loadSimBrief($user, $aircraft, [
        [
            'id'       => 100,
            'code'     => 'F',
            'name'     => 'Test Fare',
            'type'     => FareType::PASSENGER->value,
            'capacity' => 100,
            'count'    => 99,
        ],
    ]);

    // Check the flight API response
    $response = $this->get('/api/flights/'.$briefing->flight_id);
    $response->assertOk();

    $flight = $response->json('data');

    expect($flight['simbrief'])->not->toBeNull()
        ->and($flight['simbrief']['id'])->toEqual($briefing->id);

    $url = str_replace('http://', 'https://', $flight['simbrief']['url']);

    // The briefing URL is keyed by the flight ID, not the SimBrief primary key
    expect(str_ends_with($url, $briefing->flight_id.'/briefing'))->toBeTrue();

    // Retrieve the briefing via API, and then check the doctype
    $response = $this->get('/api/flights/'.$briefing->flight_id.'/briefing');
    $response->assertOk();

    $json = $response->json();

    expect($json)->not->toBeEmpty();
});

test('user bid simbrief', function (): void {
    $fares = [
        [
            'id'       => 100,
            'code'     => 'F',
            'name'     => 'Test Fare',
            'type'     => FareType::PASSENGER,
            'capacity' => 100,
            'count'    => 99,
        ],
    ];

    $userinfo = createUserData();
    $user = $userinfo['user'];
    apiAs($user);
    $aircraft = $userinfo['aircraft']->random();
    loadSimBrief($user, $aircraft, $fares);

    // Add the flight to the bid and then
    $uri = '/api/user/bids';
    $data = ['flight_id' => 'FLIGHT_ID_1'];

    $this->put($uri, $data);

    // Retrieve it
    $body = $this->get($uri);
    $body = $body->json('data')[0];

    // Make sure Simbrief is there
    expect($body['flight']['simbrief']['id'])->not->toBeNull()
        ->and($body['flight']['simbrief']['id'])->not->toBeNull()
        ->and($body['flight']['simbrief']['subfleet']['fares'])->not->toBeNull();

    $subfleet = $body['flight']['simbrief']['subfleet'];
    expect($subfleet['fares'][0]['id'])->toEqual($fares[0]['id'])
        ->and($subfleet['fares'][0]['count'])->toEqual($fares[0]['count'])
        ->and($subfleet['aircraft'])->toHaveCount(1)
        ->and($subfleet['aircraft'][0]['id'])->toEqual($aircraft->id);

});

test('user bid simbrief doesnt leak', function (): void {
    updateSetting('bids.disable_flight_on_bid', false);
    $fares = [
        [
            'id'       => 100,
            'code'     => 'F',
            'name'     => 'Test Fare',
            'type'     => FareType::PASSENGER,
            'capacity' => 100,
            'count'    => 99,
        ],
    ];

    /** @var Flight $flight */
    $flight = Flight::factory()->create();

    // Create two briefings and make sure it doesn't leak
    $userinfo2 = createUserData();
    $user2 = $userinfo2['user'];
    downloadOfp($user2, $flight, $userinfo2['aircraft']->first(), $fares);

    $userinfo = createUserData();
    $user = $userinfo['user'];
    apiAs($user);

    $briefing = downloadOfp($user, $flight, $userinfo['aircraft']->first(), $fares);

    // Add the flight to the user's bids
    $uri = '/api/user/bids';
    $data = ['flight_id' => $flight->id];

    // add for both users
    apiAs($user2);
    $body = $this->put($uri, $data)->json('data');
    expect($body)->not->toBeEmpty();

    apiAs($user);
    $body = $this->put($uri, $data)->json('data');
    expect($body)->not->toBeEmpty();

    $body = $this->get('/api/user/bids');
    $body = $body->json('data')[0];

    // Make sure Simbrief is there
    expect($body['flight']['simbrief']['id'])->not->toBeNull()
        ->and($body['flight']['simbrief']['subfleet']['fares'])->not->toBeNull()
        ->and($briefing->id)->toEqual($body['flight']['simbrief']['id']);

    $subfleet = $body['flight']['simbrief']['subfleet'];
    expect($subfleet['fares'][0]['id'])->toEqual($fares[0]['id'])
        ->and($subfleet['fares'][0]['count'])->toEqual($fares[0]['count']);
});

test('attach to pirep', function (): void {
    $userinfo = createUserData();
    $user = $userinfo['user'];

    /** @var Pirep $pirep */
    $pirep = Pirep::factory()->create([
        'user_id'        => $user->id,
        'dpt_airport_id' => 'OMAA',
        'arr_airport_id' => 'OMDB',
    ]);

    $briefing = loadSimBrief($user, $userinfo['aircraft']->first(), [
        [
            'id'       => 100,
            'code'     => 'F',
            'name'     => 'Test Fare',
            'type'     => 'P',
            'capacity' => 100,
            'count'    => 99,
        ],
    ]);

    /** @var SimBriefService $sb */
    $sb = app(SimBriefService::class);
    $sb->attachSimbriefToPirep($pirep, $briefing);

    /*
     * Checks - ACARS entries for the route are loaded
     */
    $acars = Acars::where(['pirep_id' => $pirep->id, 'type' => AcarsType::ROUTE])->get();
    expect($acars->count())->toEqual(12);

    $fix = $acars->firstWhere('name', 'BOMUP');
    expect($fix['name'])->toEqual('BOMUP')
        ->and($fix['lat'])->toEqual(24.484639)
        ->and($fix['lon'])->toEqual(54.578444)
        ->and($fix['order'])->toEqual(1);

    $briefing->refresh();

    expect($briefing->flight_id)->toBeEmpty()
        ->and($briefing->pirep_id)->toEqual($pirep->id);
});

test('simbrief create form preloads selected airports', function (): void {
    Airport::factory()->create(['id' => 'OMAA', 'icao' => 'OMAA', 'name' => 'Abu Dhabi International']);
    Airport::factory()->create(['id' => 'OMDB', 'icao' => 'OMDB', 'name' => 'Dubai International']);

    $userinfo = createUserData();
    $user = $userinfo['user'];
    $briefing = loadSimBrief($user, $userinfo['aircraft']->first(), []);

    $response = $this->actingAs($user)->get('/pireps/create?sb_id='.$briefing->id);

    $response->assertOk()
        ->assertViewHas('airport_list', fn (array $airportList): bool => ($airportList[''] ?? null) === ''
            && ($airportList['OMAA'] ?? null) === 'OMAA - Abu Dhabi International'
            && ($airportList['OMDB'] ?? null) === 'OMDB - Dubai International'
            && count($airportList) === 3);
});

test('clear expired briefs', function (): void {
    $userinfo = createUserData();
    $user = $userinfo['user'];

    $sb_ignored = SimBrief::factory()->create([
        'user_id'    => $user->id,
        'flight_id'  => 'a_flight_id',
        'pirep_id'   => 'a_pirep_id',
        'created_at' => Carbon::now('UTC')->subDays(6),
    ]);

    SimBrief::factory()->create([
        'user_id'    => $user->id,
        'flight_id'  => 'a_flight_Id',
        'created_at' => Carbon::now('UTC')->subDays(6),
    ]);

    /** @var SimBriefService $sb */
    $sb = app(SimBriefService::class);
    $sb->removeExpiredEntries();

    $all_briefs = SimBrief::all();
    expect($all_briefs->count())->toEqual(1)
        ->and($all_briefs[0]->id)->toEqual($sb_ignored->id);
});
