<?php

namespace Tests\Feature;

use App\Http\Controllers\Network\NetworkController;
use App\Models\Asns;

use App\Models\Group;
use App\Models\Organisation;
use App\Models\Prefixes;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class BulkPrefixCreateTest extends TestCase
{

    use RefreshDatabase;

    public function testBulkCreate()
    {
        /**
         * Make sure environment is safe
         */

        Http::fake([
            '*' => Http::response([ 'Fallback Error' ], 500) // Fallback
        ]);

        /**
         * Database setup and preamble
         */

        $organisation = Organisation::create([ 'name' => '' ]);

        $group = Group::create([ 'name' => 'Test' ]);

        $user = User::create([
            'name'              => 'Test McTest',
            'email'             => 'creatia@globalsecurelayer.com',
            'phone'             => null,
            'password'          => '',
            'group_id'          => $group->id,
            'organisation_id'   => $organisation->id
        ]);

        Auth::setUser($user);
        
        $asn = Asns::create([ 'asn' => 137409, 'organisation_id' => $organisation->id, 'approved' => 1 ]);

        /**
         * We're going to need some prefixes to test our list against.
         */

        $to_make = [
            [ 'address' => '103.1.0.0', 'mask' => 20 ],
            [ 'address' => '103.2.0.0', 'mask' => 16 ],
            [ 'address' => '103.1.184.0', 'mask' => 22 ],
            [ 'address' => '131.242.0.0', 'mask' => 16 ],
            [ 'address' => '147.132.0.0', 'mask' => 16 ],
            [ 'address' => '161.143.0.0', 'mask' => 16 ],
            [ 'address' => '164.108.8.0', 'mask' => 22 ],
            [ 'address' => '164.108.12.0', 'mask' => 23 ],
            [ 'address' => '164.108.16.0', 'mask' => 24 ],
            [ 'address' => '164.108.18.0', 'mask' => 24 ],
            [ 'address' => '164.108.104.0', 'mask' => 24 ]
        ];

        foreach($to_make as $i => $x) {
            DB::table("prefixes")->insert([
                [
                    'seen_at'           => Carbon::now(),
                    'still_announced'   => 1,
                    'created_source'    => 'prefix-list-uncrushed',
                    'organisation_id'   => $organisation->id,
                    'asn_id'            => $asn->id,
                    'setup_required'    => 1,
                    ...$x
                ]
            ]);
        }

        $request = new Request();

        $tmp = tmpfile();

        /**
         * We need to test the following scenarios:
         * 1. The prefix does not exist +
         * 2. The prefix does exist +
         * 3. We're creating a child +
         * 4. Invalid prefix
         *      a. Invalid mask size +
         *      b. Simply invalid +
         * 5. Reserved range +
         * 6. Private range +
         * 
         * Be sure to include a variety of filth at any end of the prefix.
         */

         $csv = 'prefix,,name
         203.1.0.0,,FAIL
         "203.1.0.0/32"     ,,FAIL
         103.1.0.22/32",,PASS
         103.1.0.0/20,,PASS
         103.1.0.0/16,,FAIL
         11.21.1.0/64,,FAIL
         300.1.0.0/32,,FAIL
         127.0.0.1,,FAIL
         240.0.0.0,,FAIL';

        fwrite($tmp, $csv);

        $tmppath = stream_get_meta_data($tmp)['uri'];

        $file = new UploadedFile(
            $tmppath,
            'list.csv',
            'text/csv',
            null,
            true
        );

        $request->files->set('list', $file);

        $outcome = json_decode((new NetworkController)->build_from_list($request)->content(), true);

        $this->assertEquals(12, Prefixes::count(), 'The number of prefixes should be 12 after the bulk import.');

        $this->assertEquals(6, $outcome['failure_count'], 'There should only be 6 failures - Even if there is 7 hypothetically, remember 1 is a duplicate');
        $this->assertEquals(2, $outcome['successful_count'], 'There should only be 2 passes');

    }

    /**
     * A row that lands inside another organisation's carve-out beneath the uploader's block is refused
     * with its own reason, and the rows beside it still go through.
     */
    public function testBulkCreateRefusesAContestedRange()
    {
        Http::fake([
            '*' => Http::response([ 'Fallback Error' ], 500) // Fallback
        ]);

        $organisation = Organisation::create([ 'name' => 'Uploader' ]);
        $other = Organisation::create([ 'name' => 'Other' ]);

        $group = Group::create([ 'name' => 'Test' ]);

        $user = User::create([
            'name'              => 'Test McTest',
            'email'             => 'creatia@globalsecurelayer.com',
            'phone'             => null,
            'password'          => '',
            'group_id'          => $group->id,
            'organisation_id'   => $organisation->id
        ]);

        Auth::setUser($user);

        $asn = Asns::create([ 'asn' => 137409, 'organisation_id' => $organisation->id, 'approved' => 1 ]);
        $other_asn = Asns::create([ 'asn' => 137410, 'organisation_id' => $other->id, 'approved' => 1 ]);

        // The uploader's /20, with somebody else's /22 carved out of it.
        Prefixes::factory()->address('103.1.0.0', 20)->ownership($organisation, $asn)->announced()->create();
        Prefixes::factory()->address('103.1.8.0', 22)->ownership($other, $other_asn)->announced()->create();

        $request = new Request();

        $tmp = tmpfile();

        $csv = 'prefix,,name
         103.1.8.0/24,,CONTESTED
         103.1.4.0/24,,PASS';

        fwrite($tmp, $csv);

        $file = new UploadedFile(
            stream_get_meta_data($tmp)['uri'],
            'list.csv',
            'text/csv',
            null,
            true
        );

        $request->files->set('list', $file);

        $outcome = json_decode((new NetworkController)->build_from_list($request)->content(), true);

        $this->assertEquals(3, Prefixes::count(), 'Only the uncontested row should have been created.');
        $this->assertEquals(1, $outcome['successful_count']);
        $this->assertEquals(1, $outcome['failure_count']);
        $this->assertSame('This range sits inside an allocation held by another organisation.', $outcome['failure']['103.1.8.0/24'], 'The contested row should say why it failed.');
    }
}
