<?php

namespace Tests\Feature;

use App\Models\Asns;
use App\Models\Goliath\GoliathAppliance;
use App\Models\Goliath\GoliathMetro;
use App\Models\Goliath\GoliathRegion;
use App\Models\Goliath\GoliathRuleset;
use App\Models\Goliath\GoliathSubregion;
use App\Models\Goliath\PrefixGoliathFirewall;
use App\Models\Goliath\PrefixGoliathFlag;
use App\Models\Goliath\RulesetType;
use App\Models\Group;

use App\Models\Organisation;
use App\Models\Prefixes;
use App\Models\PrefixSubtenants;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 *      @@@ TEST CERTIFICATION @@@
 *      This set of tests has been confirmed to be functioning as intended.
 *
 *      Jesse Cunningham, 2026-04-21 4:00 PM AEST
 */

class RulesetControllerTest extends TestCase
{

    use RefreshDatabase;

    private $organisations = [];
    private $users = [];
    private $asns = [];
    private $prefixes = [];
    private $rulesets = [];
    private $regions = [];
    private $subregions = [];
    private $metros = [];

    protected function setUp(): void
    {
        parent::setUp();

        // Setup organisations
        $this->organisations = [
            'A' => Organisation::create([ 'id' => 1, 'name' => 'A' ]),
            'B' => Organisation::create([ 'id' => 2, 'name' => 'B' ]),
            'C' => Organisation::create([ 'id' => 3, 'name' => 'C' ]),
            'D' => Organisation::create([ 'id' => 4, 'name' => 'D' ])
        ];

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

        $default_user = [
            'phone'             => null,
            'password'          => '',
            'group_id'          => $group->id,
        ];

        $this->users = [
            'A' => User::create([ ...$default_user, 'organisation_id' => $this->organisations['A']->id, 'name' => 'A', 'email' => 'creatia+1@globalsecurelayer.com' ]),
            'B' => User::create([ ...$default_user, 'organisation_id' => $this->organisations['B']->id, 'name' => 'B', 'email' => 'creatia+2@globalsecurelayer.com' ]),
            'C' => User::create([ ...$default_user, 'organisation_id' => $this->organisations['C']->id, 'name' => 'C', 'email' => 'creatia+3@globalsecurelayer.com' ]),
            'D' => User::create([ ...$default_user, 'organisation_id' => $this->organisations['D']->id, 'name' => 'D', 'email' => 'creatia+4@globalsecurelayer.com' ])
        ];

        // Setup ASNs
        $this->asns = [
            'A' => Asns::create([ 'asn' => 64512, 'organisation_id' => $this->organisations['A']->id, 'approved' => 1 ]),
            'B' => Asns::create([ 'asn' => 64513, 'organisation_id' => $this->organisations['B']->id, 'approved' => 1 ]),
            'C' => Asns::create([ 'asn' => 64514, 'organisation_id' => $this->organisations['C']->id, 'approved' => 1 ]),
            'D' => Asns::create([ 'asn' => 64515, 'organisation_id' => $this->organisations['D']->id, 'approved' => 1 ])
        ];

        // Setup prefixes
        $preset_prefix = [
            'seen_at'           => Carbon::now(),
            'still_announced'   => 1,
            'created_source'    => 'prefix-list-uncrushed',
            'setup_required'    => 0
        ];

        $this->prefixes = [
            'A_FLAGS'             => Prefixes::create([ ...$preset_prefix, 'address' => '103.1.0.0', 'mask' => 32, 'organisation_id' => $this->organisations['A']->id, 'asn_id' => $this->asns['A']->id ]),
            'A_NO_FLAGS'          => Prefixes::create([ ...$preset_prefix, 'address' => '103.1.0.1', 'mask' => 32, 'organisation_id' => $this->organisations['A']->id, 'asn_id' => $this->asns['A']->id ]),
            'A_EXISTING_CHAIN'    => Prefixes::create([ ...$preset_prefix, 'address' => '103.1.0.2', 'mask' => 32, 'organisation_id' => $this->organisations['A']->id, 'asn_id' => $this->asns['A']->id ]),
            'B_FLAGS'             => Prefixes::create([ ...$preset_prefix, 'address' => '103.2.0.0', 'mask' => 32, 'organisation_id' => $this->organisations['B']->id, 'asn_id' => $this->asns['B']->id ]),
            'B_NO_FLAGS'          => Prefixes::create([ ...$preset_prefix, 'address' => '103.2.0.1', 'mask' => 32, 'organisation_id' => $this->organisations['B']->id, 'asn_id' => $this->asns['B']->id ]),
            'B_EXISTING_CHAIN'    => Prefixes::create([ ...$preset_prefix, 'address' => '103.2.0.2', 'mask' => 32, 'organisation_id' => $this->organisations['B']->id, 'asn_id' => $this->asns['B']->id ]),
            'C_FLAGS'             => Prefixes::create([ ...$preset_prefix, 'address' => '103.3.0.0', 'mask' => 32, 'organisation_id' => $this->organisations['C']->id, 'asn_id' => $this->asns['C']->id ]),
            'C_NO_FLAGS'          => Prefixes::create([ ...$preset_prefix, 'address' => '103.3.0.1', 'mask' => 32, 'organisation_id' => $this->organisations['C']->id, 'asn_id' => $this->asns['C']->id ]),
            'C_EXISTING_CHAIN'    => Prefixes::create([ ...$preset_prefix, 'address' => '103.3.0.2', 'mask' => 32, 'organisation_id' => $this->organisations['C']->id, 'asn_id' => $this->asns['C']->id ]),
            'D_FLAGS'             => Prefixes::create([ ...$preset_prefix, 'address' => '103.4.0.0', 'mask' => 32, 'organisation_id' => $this->organisations['D']->id, 'asn_id' => $this->asns['D']->id ]),
            'D_NO_FLAGS'          => Prefixes::create([ ...$preset_prefix, 'address' => '103.4.0.1', 'mask' => 32, 'organisation_id' => $this->organisations['D']->id, 'asn_id' => $this->asns['D']->id ]),
            'D_EXISTING_CHAIN'    => Prefixes::create([ ...$preset_prefix, 'address' => '103.4.0.2', 'mask' => 32, 'organisation_id' => $this->organisations['D']->id, 'asn_id' => $this->asns['D']->id ])
        ];

        // Setup flags or lack of
        foreach(['A_FLAGS', 'B_FLAGS', 'C_FLAGS', 'D_FLAGS'] as $x) {
            PrefixGoliathFlag::create([
                'prefix_id'                 => $this->prefixes[$x]->id,
                'flag_synproxy'             => 0,
                'flag_heuristics'           => 1,
                'flag_heuristics_level'     => 3,
                'flag_fivem'                => 0
            ]);
        }

        // Setup rulesets
        $this->rulesets = [
            'A_UNIVERSAL'   => GoliathRuleset::create(['name' => 'A_UNIVERSAL', 'organisation_id' => $this->organisations['A']->id, 'type' => RulesetType::Universal->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]']),
            'A_PERSONAL'    => GoliathRuleset::create(['name' => 'A_PERSONAL', 'organisation_id' => $this->organisations['A']->id, 'type' => RulesetType::Reusable->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]']),
            'B_PERSONAL'    => GoliathRuleset::create(['name' => 'B_PERSONAL', 'organisation_id' => $this->organisations['B']->id, 'type' => RulesetType::Reusable->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]']),
            'C_PERSONAL'    => GoliathRuleset::create(['name' => 'C_PERSONAL', 'organisation_id' => $this->organisations['C']->id, 'type' => RulesetType::Reusable->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]']),
            'D_PERSONAL'    => GoliathRuleset::create(['name' => 'D_PERSONAL', 'organisation_id' => $this->organisations['D']->id, 'type' => RulesetType::Reusable->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]']),

            // A few extra ones for extra testing
            'BAD_UNIVERSAL' => GoliathRuleset::create(['name' => 'BAD_UNIVERSAL', 'organisation_id' => $this->organisations['B']->id, 'type' => RulesetType::Universal->value, 'rules' => '[{"matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},{"matchAction":"drop","x":0,"y":200}]'])
        ];

        // Setup chains where relevant
        foreach(['A', 'B', 'C', 'D'] as $x) {
            PrefixGoliathFirewall::create([
                'prefix_id'       => $this->prefixes[$x . "_EXISTING_CHAIN"]->id,
                'ruleset_id'      => $this->rulesets[$x . "_PERSONAL"]->id,
            ]);
        }

        $this->regions = [
            '1' => GoliathRegion::create([ 'name' => '1', 'telemetry_identifier' => '1', 'visible' => 1 ]),
            '2' => GoliathRegion::create([ 'name' => '2', 'telemetry_identifier' => '2', 'visible' => 1 ]),
            '3' => GoliathRegion::create([ 'name' => '3', 'telemetry_identifier' => '3', 'visible' => 1 ]),
        ];


        $this->subregions = [
            '1' => GoliathSubregion::create([ 'name' => '1.1', 'telemetry_identifier' => '1.1', 'visible' => 1, 'region_id' => $this->regions['1']->id ]),
            '2' => GoliathSubregion::create([ 'name' => '2.1', 'telemetry_identifier' => '2.1', 'visible' => 1, 'region_id' => $this->regions['2']->id ]),
            '3' => GoliathSubregion::create([ 'name' => '3.1', 'telemetry_identifier' => '3.1', 'visible' => 1, 'region_id' => $this->regions['3']->id ]),
        ];

        $this->metros = [
            '1' => GoliathMetro::create([ 'name' => '1.1.1', 'telemetry_identifier' => '1.1.1', 'visible' => 1, 'region_id' => $this->regions['1']->id, 'subregion_id' => $this->subregions['1']->id ]),
            '2' => GoliathMetro::create([ 'name' => '2.1.1', 'telemetry_identifier' => '2.1.1', 'visible' => 1, 'region_id' => $this->regions['2']->id, 'subregion_id' => $this->subregions['2']->id ]),
            '3' => GoliathMetro::create([ 'name' => '3.1.1', 'telemetry_identifier' => '3.1.1', 'visible' => 1, 'region_id' => $this->regions['3']->id, 'subregion_id' => $this->subregions['3']->id ]),
        ];

        GoliathAppliance::create([ 'name' => '1', 'organisation_id' => $this->organisations['A']->id, 'registered_at' => Carbon::now(), 'region_id' => $this->regions['1']->id, 'subregion_id' => $this->subregions['1']->id, 'metro_id' => $this->metros['1']->id, 'authorised' => 1, 'address' => '127.0.0.1' ]);
        GoliathAppliance::create([ 'name' => '2', 'organisation_id' => $this->organisations['B']->id, 'registered_at' => Carbon::now(), 'region_id' => $this->regions['2']->id, 'subregion_id' => $this->subregions['2']->id, 'metro_id' => $this->metros['2']->id, 'authorised' => 1, 'address' => '127.0.0.1' ]);
        GoliathAppliance::create([ 'name' => '3', 'organisation_id' => $this->organisations['C']->id, 'registered_at' => Carbon::now(), 'region_id' => $this->regions['3']->id, 'subregion_id' => $this->subregions['3']->id, 'metro_id' => $this->metros['3']->id, 'authorised' => 1, 'address' => '127.0.0.1' ]);
    }

    /** @@@ TEST TO DO - ADD GOTO TESTING */
    public function testCreateRuleset(): void
    {

        $this->actingAs($this->users['A'])->withoutMiddleware()->post('/api/goliath/chain', [
            "name"              => "Pass All",
            "default_action"    => "accept",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "ip",
                    "action"    => "accept",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);

        $this->actingAs($this->users['B'])->withoutMiddleware()->post('/api/goliath/chain', [
            "name"              => "Drop All",
            "default_action"    => "drop",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "ip",
                    "action"    => "drop",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);

        $this->actingAs($this->users['C'])->withoutMiddleware()->post('/api/goliath/chain', [
            "name"              => "Police All",
            "default_action"    => "police",
            "default_pps"       => 10000,
            "default_bps"       => 40960,
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"           => "ip",
                    "action"        => "police",
                    "pps"           => 10000,
                    "bps"           => 40960,
                    "x"             => 0,
                    "y"             => 0
                ]
            ]
        ]);

        // Assert they were made
        $this->assertTrue(GoliathRuleset::where('name', '=', 'Pass All')->count() === 1, 'The Pass All ruleset failed');
        $this->assertTrue(GoliathRuleset::where('name', '=', 'Drop All')->count() === 1, 'The Drop All ruleset failed');
        $this->assertTrue(GoliathRuleset::where('name', '=', 'Police All')->count() === 1, 'The Police All ruleset failed');

        // Assert they have correct ownership
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'Pass All'], ['organisation_id', '=', $this->users['A']->organisation_id]])->count() === 1, 'The Pass All ruleset was inappropriately assigned');
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'Drop All'], ['organisation_id', '=', $this->users['B']->organisation_id]])->count() === 1, 'The Drop All ruleset was inappropriately assigned');
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'Police All'], ['organisation_id', '=', $this->users['C']->organisation_id]])->count() === 1, 'The Police All ruleset was inappropriately assigned');

        // Assert the rules are correct
        $first_ruleset = json_decode(GoliathRuleset::where('name', '=', 'Pass All')->get()->first()['rules'], true);
        $second_ruleset = json_decode(GoliathRuleset::where('name', '=', 'Drop All')->get()->first()['rules'], true);
        $third_ruleset = json_decode(GoliathRuleset::where('name', '=', 'Police All')->get()->first()['rules'], true);

        $this->assertTrue($first_ruleset[0]['matchAction'] === 'accept' && $first_ruleset[0]['bpfRule'] === 'ip' && $first_ruleset[1]['matchAction'] === 'accept', 'First ruleset did not generate rules properly.');
        $this->assertTrue($second_ruleset[0]['matchAction'] === 'drop' && $second_ruleset[0]['bpfRule'] === 'ip' && $second_ruleset[1]['matchAction'] === 'drop', 'Second ruleset did not generate rules properly.');
        $this->assertTrue($third_ruleset[0]['matchAction'] === 'police' && $third_ruleset[0]['bpfRule'] === 'ip' && $third_ruleset[0]['ppsLimit'] === 10000 && $third_ruleset[0]['bpsLimit'] === 40960 && $third_ruleset[1]['ppsLimit'] === 10000 && $third_ruleset[1]['bpsLimit'] === 40960 && $third_ruleset[1]['matchAction'] === 'police', 'Third ruleset did not generate rules properly.');
    }

    public function testModifyRuleset(): void
    {
        $a_edit = $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id, [
            "name"              => "A_UNIVERSAL",
            "default_action"    => "drop",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "udp and port = 443",
                    "action"    => "accept",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);

        $b_edit = $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id, [
            "name"              => "A_HACKED",
            "default_action"    => "accept",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "udp",
                    "action"    => "drop",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);


        // There should be two rulesets - Edited one and non edited one
        $this->assertTrue($a_edit->status() === 200, 'The first edit has failed when it should not.');
        $this->assertTrue($b_edit->status() === 200, 'The second edit has failed when it should not.');

        // Assert they were made
        $this->assertTrue(GoliathRuleset::where('name', '=', 'A_UNIVERSAL')->count() === 1, 'There is no A_UNIVERSAL ruleset when there should have been');
        $this->assertTrue(GoliathRuleset::where('name', '=', 'A_HACKED')->count() === 1, 'There is no A_HACKED ruleset when there should have been');

        // Assert they have correct ownership
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'A_UNIVERSAL'], ['organisation_id', '=', $this->users['A']->organisation_id]])->count() === 1, 'The A_UNIVERSAL ruleset was inappropriately assigned');
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'A_HACKED'], ['organisation_id', '=', $this->users['B']->organisation_id]])->count() === 1, 'The A_HACKED ruleset was inappropriately assigned');

        // Assert the rules are correct
        $first_ruleset = json_decode(GoliathRuleset::where('name', '=', 'A_UNIVERSAL')->get()->first()['rules'], true);
        $second_ruleset = json_decode(GoliathRuleset::where('name', '=', 'A_HACKED')->get()->first()['rules'], true);

        $this->assertTrue($first_ruleset[0]['matchAction'] === 'accept' && $first_ruleset[0]['bpfRule'] === 'udp and port = 443' && $first_ruleset[1]['matchAction'] === 'drop', 'A_UNIVERSAL did not generate rules properly.');
        $this->assertTrue($second_ruleset[0]['matchAction'] === 'drop' && $second_ruleset[0]['bpfRule'] === 'udp' && $second_ruleset[1]['matchAction'] === 'accept', 'A_HACKED did not generate rules properly.');
    }

    public function testCreateAssignRuleset(): void
    {
        $assign = $this->actingAs($this->users['A'])->withoutMiddleware()->post('/api/goliath/chain', [
            "name"              => "Pass All",
            "prefix_id"         => $this->prefixes['A_EXISTING_CHAIN']->id,
            "default_action"    => "accept",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "ip",
                    "action"    => "accept",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);

        // Assert they were made
        $this->assertTrue(GoliathRuleset::where('name', '=', 'Pass All')->count() === 1, 'The Pass All ruleset failed');

        // Assert they have correct ownership
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'Pass All'], ['organisation_id', '=', $this->users['A']->organisation_id]])->count() === 1, 'The Pass All ruleset was inappropriately assigned');

        // Assert the rules are correct
        $first_ruleset = json_decode(GoliathRuleset::where('name', '=', 'Pass All')->get()->first()['rules'], true);

        $this->assertTrue($first_ruleset[0]['matchAction'] === 'accept' && $first_ruleset[0]['bpfRule'] === 'ip' && $first_ruleset[1]['matchAction'] === 'accept', 'First ruleset did not generate rules properly.');

        // Assert that the chain attached
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 1, 'The chain did not appropriately attach to the prefix. Multiple chains or no chains. Probably multiple.');
        $this->assertTrue(PrefixGoliathFirewall::where([['prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id], ['ruleset_id', '=', $assign->json()['chain_id']]])->get()->count() === 1, 'The wrong chain is attached.');

    }

    public function testCreateAssignRegionalRuleset(): void
    {
        $assign = $this->actingAs($this->users['A'])->withoutMiddleware()->post('/api/goliath/chain', [
            "name"              => "Pass All",
            "prefix_id"         => $this->prefixes['A_EXISTING_CHAIN']->id,
            'region_id'         => $this->regions['1']->id,
            'subregion_id'      => $this->subregions['1']->id,
            "default_action"    => "drop",
            "default_x"         => 0,
            "default_y"         => 0,
            "elements"          => [
                [
                    "bpf"       => "ip",
                    "action"    => "accept",
                    "x"         => 0,
                    "y"         => 0
                ]
            ]
        ]);

        // Assert they were made
        $this->assertTrue(GoliathRuleset::where('name', '=', 'Pass All')->count() === 1, 'The Pass All ruleset failed');

        // Assert they have correct ownership
        $this->assertTrue(GoliathRuleset::where([['name', '=', 'Pass All'], ['organisation_id', '=', $this->users['A']->organisation_id]])->count() === 1, 'The Pass All ruleset was inappropriately assigned');

        // Assert the rules are correct
        $first_ruleset = json_decode(GoliathRuleset::where('name', '=', 'Pass All')->get()->first()['rules'], true);

        $this->assertTrue($first_ruleset[0]['matchAction'] === 'accept' && $first_ruleset[0]['bpfRule'] === 'ip' && $first_ruleset[1]['matchAction'] === 'drop', 'First ruleset did not generate rules properly.');

        // Assert that the chain attached
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 2, 'The chain did not appropriately attach to the prefix. There should be two chains. We are attaching regionality.');
        $this->assertTrue(PrefixGoliathFirewall::where([['prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id], ['ruleset_id', '=', $assign->json()['chain_id']]])->get()->count() === 1, 'The wrong chain is attached.');

        // Assert correct regionality
        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id],
            ['region_id', '=', $this->regions['1']->id],
            ['subregion_id', '=', $this->subregions['1']->id],
        ])->get()->count() === 1, 'The regional chain did not attach with the correct information.');

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->whereNull('region_id')->whereNull('subregion_id')->get()->count() === 1, 'The global chain did not remain appropriately intact.');

    }

    public function testRemoveRuleset(): void
    {

        // First, remove the ruleset.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_PERSONAL']->id);

        // Assert the ruleset was adequately deleted.
        $this->assertTrue(GoliathRuleset::where('id', '=', $this->rulesets['A_PERSONAL']->id)->count() === 0, 'The ruleset was not successfully deleted as it should have been.');

        // Assert that there is still a firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->whereNull('ruleset_id')->get()->count() === 1, 'The prefix firewall entry should still exist.');

        // Check that an un-owned ruleset cannot be interfered with.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['B_PERSONAL']->id);

        // Assert the ruleset was adequately deleted.
        $this->assertTrue(GoliathRuleset::where('id', '=', $this->rulesets['B_PERSONAL']->id)->count() === 1, 'The ruleset was deleted. It should not have been given it is not owned by user A.');

        // Assert that there is still a firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->whereNotNull('ruleset_id')->get()->count() === 1, 'The prefix firewall entry should not have been interfered with.');

        // Attempt to delete a universal ruleset
        $this->actingAs($this->users['B'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id);

        // Assert the ruleset was not deleted.
        $this->assertTrue(GoliathRuleset::where('id', '=', $this->rulesets['A_UNIVERSAL']->id)->count() === 1, 'The ruleset was deleted and it should not have been.');

    }

    public function testRemoveReassignRuleset(): void
    {
        // First, reassign a ruleset to the simple universal option.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_PERSONAL']->id, [
            'reassign_chain_id' => $this->rulesets['A_UNIVERSAL']->id
        ]);

        // Assert the ruleset was adequately deleted.
        $this->assertTrue(GoliathRuleset::where('id', '=', $this->rulesets['A_PERSONAL']->id)->count() === 0, 'The ruleset was not successfully deleted as it should have been.');

        // Assert that there is still a firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->where('ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id)->get()->count() === 1, 'There is a missing prefix firewall entry for the newly assigned ruleset.');

        // Assert that there is only one firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 1, 'There should only be one entry.');

        // Second, reassign a ruleset to an option the user does not own option.
        $this->actingAs($this->users['B'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['B_PERSONAL']->id, [
            'reassign_chain_id' => $this->rulesets['C_PERSONAL']->id
        ]);

        // Assert the ruleset was not changed up.
        $this->assertTrue(GoliathRuleset::where('id', '=', $this->rulesets['B_PERSONAL']->id)->count() === 1, 'No ruleset should have ended up deleted.');

        // Assert that there is still a firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->where('ruleset_id', '=', $this->rulesets['B_PERSONAL']->id)->get()->count() === 1, 'The prefix firewall entry has went missing.');

        // Assert that there is only one firewall entry.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->get()->count() === 1, 'There should only be one entry.');
    }

    public function testRemoveRulesetGlobal(): void
    {
        // We need to have some non-global rulesets in place to test this.
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        // Assert there's the right amount
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 3, 'There should be three chains assigned before actually testing this.');

        // Remove the global ruleset and check they all disappear.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_PERSONAL']->id);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->whereNotNull('ruleset_id')->get()->count() === 0, 'Each of the chains should have been removed.');

        // Now we specifically want to test that when a universal ruleset is deleted, it's safe across organisations.
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => null,
            'subregion_id'    => null
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['A_PERSONAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['A_PERSONAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['B_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => null,
            'subregion_id'    => null
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['B_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['B_PERSONAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['B_FLAGS']->id,
            'ruleset_id'      => $this->rulesets['B_PERSONAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        // Assert there's the right amount
        $this->assertTrue(PrefixGoliathFirewall::whereIn('prefix_id', [ $this->prefixes['A_FLAGS']->id, $this->prefixes['B_FLAGS']->id ])->whereNotNull('ruleset_id')->get()->count() === 6, 'There should be six chains assigned before actually testing this.');

        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id);

        $this->assertTrue(PrefixGoliathFirewall::whereIn('prefix_id', [ $this->prefixes['A_FLAGS']->id, $this->prefixes['B_FLAGS']->id ])->whereNotNull('ruleset_id')->get()->count() === 0, 'All six of the chains should have been removed.');
    }

    public function testRemoveRulesetReassignGlobal(): void
    {
        // We need to have some non-global rulesets in place to test this.
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        // Assert there's the right amount
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 3, 'There should be three chains assigned before actually testing this.');

        // Remove the global ruleset with reassignment and make sure none disappear.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/chain/' . $this->rulesets['A_PERSONAL']->id, [
            'reassign_chain_id' => $this->rulesets['A_UNIVERSAL']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->where('ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id)->whereNotNull('ruleset_id')->get()->count() === 3, 'No chains should have been removed. There should be three universals.');
    }

    public function testRemoveRulesetPrefixAssocation(): void
    {
        // We just need to attach a regional ruleset to A_EXISTING_CHAIN to make sure things behave as we desire. This isn't the test itself.
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        // Make sure things are behaving as expected.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 3, 'There should be three chains assigned before actually testing this.');

        // First, remove a specific ruleset.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['A_EXISTING_CHAIN']->id, [
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        // Check only one was removed.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 2, 'There should only be two firewall entries left for A_EXISTING_CHAIN');
        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id],
            ['region_id', '=', $this->regions['2']->id],
            ['subregion_id', '=', $this->subregions['2']->id]
        ])->get()->count() === 0, 'The incorrect entry was removed for A_EXISTING_CHAIN');

        // Attempt to remove the last ruleset with an invalid account.
        $this->actingAs($this->users['B'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['A_EXISTING_CHAIN']->id, [
            'region_id' => null,
            'subregion_id' => null
        ]);

        // Check that the entry still exists.
        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 2, 'The User B deletion attempt should have failed due to inadequate rights.');

        // Last, remove the global ruleset. This should result in everything being removed.
        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['A_EXISTING_CHAIN']->id, [
            'region_id' => null,
            'subregion_id' => null
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->get()->count() === 0, 'There should not be any firewall entries left because the global chain was removed on A_EXISTING_CHAIN');

    }

    public function testAttachChain(): void
    {
        // Test that it will attach when owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['A_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 1, 'The chain was not attached correctly or at all.');

        // Test that it won't attach when the prefix is not owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 0, 'A chain should not attach to a prefix that the user does not own.');

        // Test that it won't attach when the chain is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_PERSONAL']->id]
        ])->get()->count() === 0, 'The user should not be able to attach a chain they do not have rights to.');

    }

    public function testAttachDuplicateChain(): void
    {
        // Test that it will attach when owned
        $a = $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => true
        ]);

        // Get identity for new
        $duplicate_id = GoliathRuleset::where('name', '=', 'A_UNIVERSAL (Copy)')->get()->first()->id;

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['A_FLAGS']->id],
            ['ruleset_id', '=', $duplicate_id]
        ])->get()->count() === 1, 'The chain was not attached correctly or at all.');

        // Check both the old and new chain exist AND it's attached.
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_UNIVERSAL%')->get()->count() === 2, 'There should be two A_UNIVERSAL% after a duplicate attach.');

        // Test that it won't attach when the prefix is not owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => true
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=',  $this->prefixes['B_FLAGS']->id)->get()->count() === 0, 'A chain should not attach to a prefix that the user does not own.');
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_PERSONAL%')->get()->count() === 1, 'You should not be able to duplicate a chain using a prefix you do not own.');

        // Test that it won't attach when the chain is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => true
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=',  $this->prefixes['B_FLAGS']->id)->get()->count() === 0, 'The user should not be able to attach a chain they do not have rights to.');
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_PERSONAL%')->get()->count() === 1, 'A chain should not be duplicated when it is not owned.');

    }

    public function testAttachRegionalChain(): void
    {
        // Attach a global first.
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        // Test that it will attach when owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false,
            'region_id' => $this->regions['2']->id,
            'subregion_id' => $this->subregions['2']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['A_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 2, 'The chain was not attached correctly or at all.');

        // Test that it won't attach when the prefix is not owned
                
        // Attach a global first.
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false,
            'region_id' => $this->regions['1']->id,
            'subregion_id' => $this->subregions['1']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 1, 'A chain should not attach to a prefix that the user does not own.');

        // Test that it won't attach when the chain is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => false,
            'region_id' => $this->regions['3']->id,
            'subregion_id' => $this->subregions['3']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_PERSONAL']->id]
        ])->get()->count() === 0, 'The user should not be able to attach a chain they do not have rights to.');

    }

    public function testAttachRegionalDuplicateChain(): void
    {
        // Test that it will attach when owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => true,
            'region_id' => $this->regions['1']->id,
            'subregion_id' => $this->subregions['1']->id
        ]);

        // Get identity for new
        $duplicate_id = GoliathRuleset::where('name', '=', 'A_UNIVERSAL (Copy)')->get()->first()->id;

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['A_FLAGS']->id],
            ['ruleset_id', '=', $duplicate_id]
        ])->get()->count() === 1, 'The chain was not attached correctly or at all.');

        // Check both the old and new chain exist AND it's attached.
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_UNIVERSAL%')->get()->count() === 2, 'There should be two A_UNIVERSAL% after a duplicate attach.');

        // Test that it won't attach when the prefix is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['B_PERSONAL']->id,
            'make_duplicate' => false
        ]);

        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => true,
            'region_id' => $this->regions['1']->id,
            'subregion_id' => $this->subregions['1']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=',  $this->prefixes['B_FLAGS']->id)->get()->count() === 1, 'A chain should not attach to a prefix that the user does not own.');
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_PERSONAL%')->get()->count() === 1, 'You should not be able to duplicate a chain using a prefix you do not own.');

        // Test that it won't attach when the chain is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => true,
            'region_id' => $this->regions['1']->id,
            'subregion_id' => $this->subregions['1']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=',  $this->prefixes['B_FLAGS']->id)->get()->count() === 1, 'The user should not be able to attach a chain they do not have rights to.');
        $this->assertTrue(GoliathRuleset::where('name', 'like', 'A_PERSONAL%')->get()->count() === 1, 'A chain should not be duplicated when it is not owned.');

    }

    public function testAttachFlaglessChain(): void
    {
        // Test that it will attach when owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['A_NO_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['A_NO_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 1, 'The chain was not attached correctly or at all.');

        $this->assertTrue(PrefixGoliathFlag::where('prefix_id', '=',  $this->prefixes['A_NO_FLAGS']->id)->get()->count() === 1, 'Flags need to be generated when a chain is attached.');

        // Test that it won't attach when the prefix is not owned
        $this->actingAs($this->users['A'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_NO_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_UNIVERSAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_NO_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_UNIVERSAL']->id]
        ])->get()->count() === 0, 'A chain should not attach to a prefix that the user does not own.');

        $this->assertTrue(PrefixGoliathFlag::where('prefix_id', '=',  $this->prefixes['B_NO_FLAGS']->id)->get()->count() === 0, 'Flags should not be generated with an invalid operation.');

        // Test that it won't attach when the chain is not owned
        $this->actingAs($this->users['B'])->withoutMiddleware()->put('/api/goliath/prefix/chain/' . $this->prefixes['B_NO_FLAGS']->id, [
            'chain_id' => $this->rulesets['A_PERSONAL']->id,
            'make_duplicate' => false
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where([
            ['prefix_id', '=',  $this->prefixes['B_NO_FLAGS']->id],
            ['ruleset_id', '=', $this->rulesets['A_PERSONAL']->id]
        ])->get()->count() === 0, 'The user should not be able to attach a chain they do not have rights to.');

        $this->assertTrue(PrefixGoliathFlag::where('prefix_id', '=',  $this->prefixes['B_NO_FLAGS']->id)->get()->count() === 0, 'Flags should not be generated with an invalid operation.');

    }

    public function testRetrieveListOfRulesets(): void
    {

        /**
         * Standard List
         */

        // Keeps track of what tests should return.
        $testing_manifest = [
            'response_1' => [ 'A_UNIVERSAL', 'A_PERSONAL' ],
            'response_2' => [ 'A_UNIVERSAL', 'B_PERSONAL', 'BAD_UNIVERSAL' ]
        ];

        // Our actual tests.
        $response_1 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name');
        $response_2 = $this->actingAs($this->users['B'])->withoutMiddleware()->get('/api/goliath/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name');

        // Calculate results.
        foreach($testing_manifest as $x => $y) {
            
            $count = 0;

            foreach($$x->json()['data'] as $row) {
                if(array_search($row['name'], $y) !== false) {
                    $count++;
                }
            }

            $this->assertEquals(sizeof($y), $count, "The number of rows should be " . sizeof($y) . " but it was $count. Incorrect rulesets returned for $x.");
        }
        
        /**
         * Prefix Specific List
         */

        // Add universal onto `A` prefix
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["A_EXISTING_CHAIN"]->id,
            'ruleset_id'      => $this->rulesets["BAD_UNIVERSAL"]->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        // Keep track of what tests should return.
        $testing_manifest = [
            'response_1' => [ 'A_PERSONAL', 'BAD_UNIVERSAL' ], // A prefix should ALWAYS show the rules on it when they're not owned since they have ownership of that prefix and need to be able to action changes.
            'response_2' => [ 'B_PERSONAL' ]
        ];

        // Our actual tests.
        $response_1 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/prefix/' . $this->prefixes['A_EXISTING_CHAIN']->id . '/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name', []);
        $response_2 = $this->actingAs($this->users['B'])->withoutMiddleware()->get('/api/goliath/prefix/' . $this->prefixes['B_EXISTING_CHAIN']->id . '/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name', []);

        $region_count = 0;

        // Calculate results.
        foreach($testing_manifest as $x => $y) {
            
            $count = 0;

            foreach($$x->json()['data'] as $row) {
                if(array_search($row['name'], $y) !== false) {

                    if($row['region_id'] !== null) {
                        $region_count++;
                    }

                    $count++;
                }
            }

            $this->assertEquals(sizeof($y), $count, "The number of rows should be " . sizeof($y) . " but it was $count. Incorrect rulesets returned for $x.");
        }

        $this->assertEquals(1, $region_count, 'You should have had only one regional chain returned. The amount returned was ' . $region_count);

        /**
         * Subtenanted Prefix
         */

        // Add subtenant entry in.

        PrefixSubtenants::insert([[
            'also_subnets'  => 1,
            'prefix_id'     => $this->prefixes['A_EXISTING_CHAIN']->id,
            'subtenant_id'  => $this->organisations['C']->id
        ]]);

        // Keep track of what tests should return.
        $testing_manifest = [
            'response_1' => [ 'A_PERSONAL', 'BAD_UNIVERSAL' ]
        ];

        // Our actual tests.
        $response_1 = $this->actingAs($this->users['C'])->withoutMiddleware()->get('/api/goliath/prefix/' . $this->prefixes['A_EXISTING_CHAIN']->id . '/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name', []);

        // Calculate results.
        foreach($testing_manifest as $x => $y) {
            
            $count = 0;

            foreach($$x->json()['data'] as $row) {
                if(array_search($row['name'], $y) !== false) {
                    $count++;
                }
            }

            $this->assertEquals(sizeof($y), $count, "The number of rows should be " . sizeof($y) . " but it was $count. Incorrect rulesets returned for $x.");
        }

        /**
         * Check that the user cannot access lists for prefixes they do not own.
         */

        $response_1 = $this->actingAs($this->users['D'])->withoutMiddleware()->get('/api/goliath/prefix/' . $this->prefixes['A_EXISTING_CHAIN']->id . '/chain?draw=0&start=0&length=100&order_direction=asc&order_column=name', []);

        $this->assertTrue($response_1->status() !== 200, "User was able to access list for prefix they do not own.");
    }

    public function testRetrieveRulesetInformation(): void
    {

        // Make the A ruleset more interesting for testing purposes.
        $a_universal_interesting = GoliathRuleset::create([
            'name'              => 'A_UNIVERSAL_INTERESTING',
            'organisation_id'   => $this->organisations['A']->id,
            'type'              => RulesetType::Universal->value,
            'rules'             => '[
                {"name":"Rule 1","matchAction":"accept","x":0,"y":0,"bpfRule":"udp"},
                {"name":"Rule 2","matchAction":"drop","x":0,"y":200},
                {"matchAction":"police","ppsLimit":10000,"bpsLimit":10000,"x":0,"y":400}
            ]'
        ]);

        // 1. Retrieve a universal ruleset as user B, which means you will not have can-edit permissions.

        $response_1 = $this->actingAs($this->users['B'])->withoutMiddleware()->get('/api/goliath/chain/' . $a_universal_interesting->id)->json();
        $response_1_rules = json_decode($a_universal_interesting['rules'], true);

        foreach([ $response_1_rules[0], $response_1_rules[1] ] as $i => $rule) {
            
            $element = $response_1['elements'][$i];

            $this->assertEquals($rule['name'], $element['name'], "Rule $i does not have the expected name.");
            $this->assertEquals($rule['x'], $element['x'], "Rule $i does not have the expected x.");
            $this->assertEquals($rule['y'], $element['y'], "Rule $i does not have the expected y.");
            $this->assertEquals($rule['matchAction'], $element['action'], "Rule $i does not have the expected action.");

            if($rule['matchAction'] === 'police' && $element['action'] === 'police') {
                $this->assertEquals($rule['ppsLimit'], $element['pps'], "Rule $i does not have the expected PPS.");
                $this->assertEquals($rule['bpsLimit'], $element['bps'], "Rule $i does not have the expected BPS.");
            }
        }


        $this->assertEquals($response_1_rules[2]['x'], $response_1['default_x'], "Rule default does not have the expected x.");
        $this->assertEquals($response_1_rules[2]['y'], $response_1['default_y'], "Rule default does not have the expected y.");
        $this->assertEquals($response_1_rules[2]['matchAction'], $response_1['default_action'], "Rule default does not have the expected action.");

        if($response_1_rules[2]['matchAction'] === 'police' && $response_1['default_action'] === 'police') {
            $this->assertEquals($response_1_rules[2]['ppsLimit'], $response_1['default_pps'], "Rule default does not have the expected PPS.");
            $this->assertEquals($response_1_rules[2]['bpsLimit'], $response_1['default_bps'], "Rule default does not have the expected BPS.");
        }

        $this->assertEquals(false, $response_1['can_edit'], "This case should not allow the user to edit it.");
        
        // 2. Retrieve an owned ruleset as user A, which means you will have can-edit permissions.
        $response_2 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/' . $a_universal_interesting->id)->json();
        $this->assertEquals(true, $response_2['can_edit'], "This case should allow the user to edit it.");

        // 3. Get a ruleset using a prefix-id that is not owned by the user
        $response_3 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/' . $a_universal_interesting->id . '?prefix_id=' . $this->prefixes['B_EXISTING_CHAIN']->id);
        $this->assertTrue($response_3->status() !== 200, 'This attempt to access the chain should have failed on the prefix_id basis.');

        // 4. Get a ruleset using a ruleset-id that is not visible to the user.
        $response_4 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['BAD_UNIVERSAL']->id);
        $response_5 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['B_PERSONAL']->id);

        $this->assertTrue($response_4->status() !== 200, 'This attempt to access the chain should have failed on the prefix_id basis.');
        $this->assertTrue($response_5->status() !== 200, 'This attempt to access the chain should have failed on the prefix_id basis.');

        // 5. Check if User B can access Universal A (They should be able to, as Universal A belongs to first org)
        $response_6 = $this->actingAs($this->users['B'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id);

        $this->assertTrue($response_6->status() === 200, 'Any universal rulesets owned by the first organisation in Creatia should be visible to any and all users.');
    }

    public function testRetrieveListOfRulesetOptions(): void
    {
        /**
         *      1. Options in general.
         *      2. A prefix with no rulesets assigned. `A_FLAGS`
         *      3. A prefix with a global ruleset assigned. `A_EXISTING_CHAIN`
         *      4. A prefix with a regional ruleset assigned. `A_EXISTING_CHAIN`
         *      5. User attempts to access a prefix they do not own. `B_EXISTING_CHAIN`
         */


        $response_1 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/options', []);

        $response_2 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/options?prefix_id=' . $this->prefixes['A_FLAGS']->id, []);

        $response_3 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/options?prefix_id=' . $this->prefixes['A_EXISTING_CHAIN']->id, []);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["A_EXISTING_CHAIN"]->id,
            'ruleset_id'      => $this->rulesets["A_UNIVERSAL"]->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        $response_4 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/options?prefix_id=' . $this->prefixes['A_EXISTING_CHAIN']->id, []);

        $response_5 = $this->actingAs($this->users['D'])->withoutMiddleware()->get('/api/goliath/chain/options?prefix_id=' . $this->prefixes['A_FLAGS']->id, []);

        $testing_manifest = [
            'response_1' => [ 
                'chains' => [ 'A_UNIVERSAL', 'A_PERSONAL' ],
                'region_chains' => null,
                'global_set' => null
            ],
            'response_2' => [ 
                'chains' => [ 'A_UNIVERSAL', 'A_PERSONAL' ],
                'region_chains' => null,
                'global_set' => null
            ],
            'response_3' => [ 
                'chains' => [ 'A_UNIVERSAL', 'A_PERSONAL' ],
                'region_chains' => [[ 'region_id' => null, 'subregion_id' => null ]],
                'global_set' => true
            ],
            'response_4' => [ 
                'chains' => [ 'A_UNIVERSAL', 'A_PERSONAL' ],
                'region_chains' => [
                    [ 'region_id' => null, 'subregion_id' => null ],
                    [ 'region_id' => $this->regions['1']->id, 'subregion_id' => $this->subregions['1']->id ]
                ],
                'global_set' => true
            ]
        ];

        // Calculate results.
        foreach($testing_manifest as $x => $y) {
            
            $data = $$x->json();

            // Confirm chains match
            $chain_count = 0;
            $chain_already_found = [];

            foreach($data['chains'] as $row) {
                if(array_search($row['name'], $y['chains']) !== false && array_search($row['id'], $chain_already_found) === false) {
                    $chain_already_found[] = $row['id'];
                    $chain_count++;
                }
            }

            // Confirm regionality matches
            $regionality_count = 0;
            $regionality_already_found = [];

            if($y['region_chains'] !== null && isset($data['region_chains'])) {
                foreach($data['region_chains'] as $row) {
                    foreach($y['region_chains'] as $region) {
                        if($region['region_id'] == $row['region_id'] && $region['subregion_id'] == $row['subregion_id'] && array_search($row['region_id'] . '-' . $row['subregion_id'], $regionality_already_found) === false) {
                            $regionality_already_found[] = $row['region_id'] . '-' . $row['subregion_id'];
                            $regionality_count++;
                        }
                    }
                }
            }

            // Confirm global matches
            $this->assertEquals(sizeof($y['chains']), $chain_count, 'The number of chains was inaccurate for ' . $x);

            $this->assertEquals($y['region_chains'] === null ? 0 : sizeof($y['region_chains']), $regionality_count, 'The returned regionality was incorrect for ' . $x);

            if(!isset($data['global_set'])) {
                $this->assertEquals($y['global_set'], null, 'The status of the set global was inaccurate for ' . $x);
            }
            else {
                $this->assertEquals($y['global_set'], $data['global_set'], 'The status of the set global was inaccurate for ' . $x);                
            }
            
        }

        $this->assertTrue($response_5->status() !== 200, 'The user attempted to access a prefixes chains where they do not own the prefix. They succeeded. This cannot happen.');
    }

    public function testRetrieveRulesetPrefixes(): void
    {

        /**
         * Preamble / Setup
         */

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["A_FLAGS"]->id,
            'ruleset_id'      => $this->rulesets["A_UNIVERSAL"]->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["A_NO_FLAGS"]->id,
            'ruleset_id'      => $this->rulesets["A_UNIVERSAL"]->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["B_FLAGS"]->id,
            'ruleset_id'      => $this->rulesets["B_PERSONAL"]->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["B_NO_FLAGS"]->id,
            'ruleset_id'      => $this->rulesets["B_PERSONAL"]->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixSubtenants::insert([[
            'also_subnets'  => 1,
            'prefix_id'     => $this->prefixes['C_NO_FLAGS']->id,
            'subtenant_id'  => $this->organisations['A']->id
        ]]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes["C_NO_FLAGS"]->id,
            'ruleset_id'      => $this->rulesets["A_UNIVERSAL"]->id
        ]);

        /**
         * Standard Listings
         */

        $testing_manifest = [
            'response_1' => [
                $this->prefixes['A_FLAGS']->address . '/' . $this->prefixes['A_FLAGS']->mask,
                $this->prefixes['A_NO_FLAGS']->address . '/' . $this->prefixes['A_NO_FLAGS']->mask,
                $this->prefixes['C_NO_FLAGS']->address . '/' . $this->prefixes['C_NO_FLAGS']->mask
            ],
            'response_2' => [
                $this->prefixes['B_FLAGS']->address . '/' . $this->prefixes['B_FLAGS']->mask,
                $this->prefixes['B_NO_FLAGS']->address . '/' . $this->prefixes['B_NO_FLAGS']->mask,
                $this->prefixes['B_EXISTING_CHAIN']->address . '/' . $this->prefixes['B_EXISTING_CHAIN']->mask
            ]
        ];

        // Our actual tests.
        $response_1 = $this->actingAs($this->users['A'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id . '/prefix/summary');
        $response_2 = $this->actingAs($this->users['B'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['B_PERSONAL']->id . '/prefix/summary');

        // Calculate results.
        foreach($testing_manifest as $x => $y) {
            
            $count = 0;
            $already_found = [];

            foreach($$x->json() as $row) {
                if(array_search($row['prefix'], $y) !== false && array_search($row['id'], $already_found) === false) {
                    $already_found[] = $row['id'];
                    $count++;
                }
            }

            $this->assertEquals(sizeof($y), $count, "The number of rows should be " . sizeof($y) . " but it was $count. Incorrect rulesets returned for $x.");
        }

        // Access escalation tests
        $response_3 = $this->actingAs($this->users['D'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['A_UNIVERSAL']->id . '/prefix/summary');

        $this->assertEquals(0, sizeof($response_3->json()), 'The third response should not reveal any prefixes.');

        $response_4 = $this->actingAs($this->users['C'])->withoutMiddleware()->get('/api/goliath/chain/' . $this->rulesets['A_PERSONAL']->id . '/prefix/summary');

        $this->assertNotEquals(200, $response_4->status(), 'The fourth response should should fail.');

    }

    public function testDeletePrefixRuleset(): void
    {
        // Test 1 - Detach global ruleset, causing all others to disappear.
        
        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['A_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['2']->id,
            'subregion_id'    => $this->subregions['2']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->count() === 3, 'Incorrect number of firewall objects before test.');

        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['A_EXISTING_CHAIN']->id, []);

        $this->assertEquals(0, PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['A_EXISTING_CHAIN']->id)->count(), 'The firewall entries should have all been deleted.');

        // Test 2 - Detach regional ruleset, only causing the regional ruleset to disappear.

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['B_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['A_UNIVERSAL']->id,
            'region_id'       => $this->regions['1']->id
        ]);

        PrefixGoliathFirewall::create([
            'prefix_id'       => $this->prefixes['B_EXISTING_CHAIN']->id,
            'ruleset_id'      => $this->rulesets['B_PERSONAL']->id,
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        $this->assertTrue(PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->count() === 3, 'Incorrect number of firewall objects before test.');

        $this->actingAs($this->users['B'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['B_EXISTING_CHAIN']->id, [
            'region_id' => $this->regions['1']->id
        ]);

        $this->assertEquals(2, PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->count(), 'There should be two remaining firewall entries.');

        $this->actingAs($this->users['B'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['B_EXISTING_CHAIN']->id, [
            'region_id'       => $this->regions['1']->id,
            'subregion_id'    => $this->subregions['1']->id
        ]);

        $this->assertEquals(1, PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->count(), 'There should be one remaining firewall entry.');

        // Test 3 - The user attempts to remove a ruleset from a prefix they do not own.
        $response_3 = $this->actingAs($this->users['C'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['B_EXISTING_CHAIN']->id, []);

        $this->assertEquals(409, $response_3->status(), "User must not be able to interfere with a prefix they do not own.");

        // Test 4 - The user removes a prefix that is subtenanted to them.
        PrefixSubtenants::insert([[
            'also_subnets'  => 1,
            'prefix_id'     => $this->prefixes['B_EXISTING_CHAIN']->id,
            'subtenant_id'  => $this->organisations['A']->id
        ]]);

        $this->actingAs($this->users['A'])->withoutMiddleware()->delete('/api/goliath/prefix/chain/' . $this->prefixes['B_EXISTING_CHAIN']->id, []);

        $this->assertEquals(0, PrefixGoliathFirewall::where('prefix_id', '=', $this->prefixes['B_EXISTING_CHAIN']->id)->count(), 'All firewall entries for this prefix should be removed by now. Subtenants can remove too.');

    }

}
