<?php

namespace Tests\Feature;

use App\Models\Asns;
use App\Models\Group;

use App\Models\Organisation;
use App\Models\Prefixes;
use App\Models\User;
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 PrefixFunctionalityTest extends TestCase
{

    use RefreshDatabase;

    private $organisations = [];
    private $users = [];
    private $asns = [];

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

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

        // 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' ])
        ];

        // 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 ])
        ];

    }


    public function testDeleteScenario1(): void // All announced except last, A contains B - When A is removed, B should be removed.
    {

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(5, Prefixes::all(), 'There needs to be five prefixes prior to testing scenario 1');

        // Run the test. 
        $prefix_a->delete();

        $this->assertCount(3, Prefixes::all(), 'There should be three prefixes left after testing scenario 1');
        $this->assertCount(0, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id ])->get(), 'The prefixes should not still be accessible.');

    }

    public function testDeleteScenario2(): void // All announced except last, A contains B, B contains C - When B is removed, C should remain.
    {

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->create();

        $prefix_c = Prefixes::factory()
            ->address('153.0.0.0', 22)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(6, Prefixes::all(), 'There needs to be six prefixes prior to testing scenario 2');

        // Run the test. 
        $prefix_b->delete();

        $this->assertCount(5, Prefixes::all(), 'There should be five prefixes left after testing scenario 2');
        $this->assertCount(2, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id, $prefix_c->id ])->get(), 'The prefixes accessible should number two.');

    }

    public function testDeleteScenario3(): void // All announced except last, A contains B, B contains C, C contains D - When C is removed, D should remain.
    {

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->create();

        $prefix_c = Prefixes::factory()
            ->address('153.0.0.0', 22)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->create();

        $prefix_d = Prefixes::factory()
            ->address('153.0.0.0', 23)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->parent($prefix_c)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(7, Prefixes::all(), 'There needs to be seven prefixes prior to testing scenario 3');

        // Run the test. 
        $prefix_c->delete();

        $this->assertCount(6, Prefixes::all(), 'There should be six prefixes left after testing scenario 3');
        $this->assertCount(3, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id, $prefix_c->id, $prefix_d->id ])->get(), 'The prefixes accessible should number three.');

    }

    public function testDeleteScenario4(): void // All announced except last, A contains B, B contains C, C contains D - When B is removed, D should remain.
    {
        /**
         *          - 5. None announced except C, A contains B, B contains C, C contains D - When C is removed, D should be as well.
         *          - 6. None announced except B, A contains B, B contains C, C contains D - When B is removed, C and D should be removed.
         */

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->create();

        $prefix_c = Prefixes::factory()
            ->address('153.0.0.0', 22)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->create();

        $prefix_d = Prefixes::factory()
            ->address('153.0.0.0', 23)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->parent($prefix_c)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(7, Prefixes::all(), 'There needs to be seven prefixes prior to testing scenario 4');

        // Run the test. 
        $prefix_b->delete();

        $this->assertCount(6, Prefixes::all(), 'There should be six prefixes left after testing scenario 4');
        $this->assertCount(3, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id, $prefix_c->id, $prefix_d->id ])->get(), 'The prefixes accessible should number three.');

    }

    public function testDeleteScenario5(): void // None announced except C, A contains B, B contains C, C contains D - When C is removed, D should be as well.
    {

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->create();

        $prefix_c = Prefixes::factory()
            ->address('153.0.0.0', 22)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->create();

        $prefix_d = Prefixes::factory()
            ->address('153.0.0.0', 23)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->parent($prefix_c)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(7, Prefixes::all(), 'There needs to be seven prefixes prior to testing scenario 5');

        // Run the test. 
        $prefix_c->delete();

        $this->assertCount(5, Prefixes::all(), 'There should be five prefixes left after testing scenario 5');
        $this->assertCount(2, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id, $prefix_c->id, $prefix_d->id ])->get(), 'The prefixes accessible should number two.');

    }

    public function testDeleteScenario6(): void // None announced except B, A contains B, B contains C, C contains D - When B is removed, C and D should be removed.
    {

        // Create a bunch of control prefixes

        Prefixes::factory()->address('253.0.0.0', 20)->ownership($this->organisations['B'], $this->asns['B'])->announced()->create();
        Prefixes::factory()->address('254.0.0.0', 22)->ownership($this->organisations['B'], $this->asns['B'])->notAnnounced()->create();
        Prefixes::factory()->address('250.0.0.0', 20)->ownership($this->organisations['A'], $this->asns['A'])->announced()->create();

        // Create scenario prefixes 

        $prefix_a = Prefixes::factory()
            ->address('153.0.0.0', 20)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->create();

        $prefix_b = Prefixes::factory()
            ->address('153.0.0.0', 21)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->announced()
            ->parent($prefix_a)
            ->create();

        $prefix_c = Prefixes::factory()
            ->address('153.0.0.0', 22)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->create();

        $prefix_d = Prefixes::factory()
            ->address('153.0.0.0', 23)
            ->ownership($this->organisations['A'], $this->asns['A'])
            ->notAnnounced()
            ->parent($prefix_a)
            ->parent($prefix_b)
            ->parent($prefix_c)
            ->create();

        // Assert pre-test conditions.
        $this->assertCount(7, Prefixes::all(), 'There needs to be seven prefixes prior to testing scenario 6');

        // Run the test. 
        $prefix_b->delete();

        $this->assertCount(4, Prefixes::all(), 'There should be five prefixes left after testing scenario 6');
        $this->assertCount(1, Prefixes::whereIn('id', [ $prefix_a->id, $prefix_b->id, $prefix_c->id, $prefix_d->id ])->get(), 'The prefixes accessible should number one.');

    }

}
