<?php

namespace Tests\Feature;

use App\Models\Asns;
use App\Models\Organisation;
use App\Models\Prefixes;
use App\Models\Supernets;
use Tests\TestCase;

class PrefixSortTest extends TestCase
{

    private $organisations = [];
    private $asns = [];
    private $prefixes = [];
    private $prefix_results = [];

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

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

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

        $this->prefixes = [
            'A1' => [
                'IRRELEVANT' => Prefixes::factory()->address('112.0.0.0', 15)->ownership($this->organisations['A'], $this->asns['A1'])->create(),

                'MAIN_PARENT_1' => Prefixes::factory()->address('112.0.0.0', 16)->ownership($this->organisations['A'], $this->asns['A1'])->create(),

                'CHILD_1_1' => Prefixes::factory()->address('112.0.128.0', 20)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_1_2' => Prefixes::factory()->address('112.0.128.0', 21)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_1_3' => Prefixes::factory()->address('112.0.136.0', 21)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_1_4' => Prefixes::factory()->address('112.0.136.0', 32)->ownership($this->organisations['A'], $this->asns['A1'])->create(),

                'MAIN_PARENT_2' => Prefixes::factory()->address('114.0.0.0', 16)->ownership($this->organisations['A'], $this->asns['A1'])->create(),

                'CHILD_2_1' => Prefixes::factory()->address('114.0.128.0', 20)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_2_2' => Prefixes::factory()->address('114.0.128.0', 21)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_2_3' => Prefixes::factory()->address('114.0.136.0', 21)->ownership($this->organisations['A'], $this->asns['A1'])->create(),
                'CHILD_2_4' => Prefixes::factory()->address('114.0.136.0', 32)->ownership($this->organisations['A'], $this->asns['A1'])->create()
            ],
            'B1' => [
                'IRRELEVANT' => Prefixes::factory()->address('122.0.0.0', 15)->ownership($this->organisations['B'], $this->asns['B1'])->create(),

                'MAIN_PARENT_1' => Prefixes::factory()->address('122.0.0.0', 16)->ownership($this->organisations['B'], $this->asns['B1'])->create(),

                'CHILD_1_1' => Prefixes::factory()->address('122.0.128.0', 20)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_1_2' => Prefixes::factory()->address('122.0.128.0', 21)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_1_3' => Prefixes::factory()->address('122.0.136.0', 21)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_1_4' => Prefixes::factory()->address('122.0.136.0', 32)->ownership($this->organisations['B'], $this->asns['B1'])->create(),

                'MAIN_PARENT_2' => Prefixes::factory()->address('124.0.0.0', 16)->ownership($this->organisations['B'], $this->asns['B1'])->create(),

                'CHILD_2_1' => Prefixes::factory()->address('124.0.128.0', 20)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_2_2' => Prefixes::factory()->address('124.0.128.0', 21)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_2_3' => Prefixes::factory()->address('124.0.136.0', 21)->ownership($this->organisations['B'], $this->asns['B1'])->create(),
                'CHILD_2_4' => Prefixes::factory()->address('124.0.136.0', 32)->ownership($this->organisations['B'], $this->asns['B1'])->create()
            ],
            'C1' => [
                'IRRELEVANT' => Prefixes::factory()->address('132.0.0.0', 15)->ownership($this->organisations['C'], $this->asns['C1'])->create(),

                'MAIN_PARENT_1' => Prefixes::factory()->address('132.0.0.0', 16)->ownership($this->organisations['C'], $this->asns['C1'])->create(),

                'CHILD_1_1' => Prefixes::factory()->address('132.0.128.0', 20)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_1_2' => Prefixes::factory()->address('132.0.128.0', 21)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_1_3' => Prefixes::factory()->address('132.0.136.0', 21)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_1_4' => Prefixes::factory()->address('132.0.136.0', 32)->ownership($this->organisations['C'], $this->asns['C1'])->create(),

                'MAIN_PARENT_2' => Prefixes::factory()->address('134.0.0.0', 16)->ownership($this->organisations['C'], $this->asns['C1'])->create(),

                'CHILD_2_1' => Prefixes::factory()->address('134.0.128.0', 20)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_2_2' => Prefixes::factory()->address('134.0.128.0', 21)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_2_3' => Prefixes::factory()->address('134.0.136.0', 21)->ownership($this->organisations['C'], $this->asns['C1'])->create(),
                'CHILD_2_4' => Prefixes::factory()->address('134.0.136.0', 32)->ownership($this->organisations['C'], $this->asns['C1'])->create()
            ],
            'D1' => [
                'IRRELEVANT' => Prefixes::factory()->address('142.0.0.0', 15)->ownership($this->organisations['D'], $this->asns['D1'])->create(),

                'MAIN_PARENT_1' => Prefixes::factory()->address('142.0.0.0', 16)->ownership($this->organisations['D'], $this->asns['D1'])->create(),

                'CHILD_1_1' => Prefixes::factory()->address('142.0.128.0', 20)->ownership($this->organisations['D'], $this->asns['D1'])->create(),
                'CHILD_1_2' => Prefixes::factory()->address('142.0.128.0', 21)->ownership($this->organisations['D'], $this->asns['D1'])->create(),
                'CHILD_1_3' => Prefixes::factory()->address('142.0.136.0', 32)->ownership($this->organisations['D'], $this->asns['D1'])->create(),
            ],
            'D2' => [
                'MAIN_PARENT_2' => Prefixes::factory()->address('144.0.0.0', 16)->ownership($this->organisations['D'], $this->asns['D2'])->create(),

                'CHILD_2_1' => Prefixes::factory()->address('144.0.128.0', 20)->ownership($this->organisations['D'], $this->asns['D2'])->create(),
                'CHILD_2_2' => Prefixes::factory()->address('144.0.128.0', 21)->ownership($this->organisations['D'], $this->asns['D2'])->create(),
                'CHILD_2_3' => Prefixes::factory()->address('144.0.136.0', 21)->ownership($this->organisations['D'], $this->asns['D2'])->create(),
                'CHILD_2_4' => Prefixes::factory()->address('144.0.136.0', 32)->ownership($this->organisations['D'], $this->asns['D2'])->create(),

                // Sits inside D1's 142.x space but belongs to ASN D2 - must gain no relations across the ASN boundary.
                'CHILD_2_5' => Prefixes::factory()->address('142.0.136.0', 21)->ownership($this->organisations['D'], $this->asns['D2'])->create(),
            ]
        ];

        $this->prefix_results = [
            'A1' => [
                'IRRELEVANT' => [ 'children' => [], 'parents' => [] ],

                'MAIN_PARENT_1' => [ 'children' => [ 'CHILD_1_1', 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [] ],

                'CHILD_1_1' => [ 'children' => [ 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1' ] ],
                'CHILD_1_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_3' => [ 'children' => [ 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1', 'CHILD_1_3' ] ],

                'MAIN_PARENT_2' => [ 'children' => [ 'CHILD_2_1', 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [] ],

                'CHILD_2_1' => [ 'children' => [ 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2' ] ],
                'CHILD_2_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_3' => [ 'children' => [ 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1', 'CHILD_2_3' ] ],
            ],
            'B1' => [
                'IRRELEVANT' => [ 'children' => [], 'parents' => [] ],

                'MAIN_PARENT_1' => [ 'children' => [ 'CHILD_1_1', 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [] ],

                'CHILD_1_1' => [ 'children' => [ 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1' ] ],
                'CHILD_1_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_3' => [ 'children' => [ 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1', 'CHILD_1_3' ] ],

                'MAIN_PARENT_2' => [ 'children' => [ 'CHILD_2_1', 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [] ],

                'CHILD_2_1' => [ 'children' => [ 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2' ] ],
                'CHILD_2_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_3' => [ 'children' => [ 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1', 'CHILD_2_3' ] ],
            ],
            'C1' => [
                'IRRELEVANT' => [ 'children' => [], 'parents' => [] ],

                'MAIN_PARENT_1' => [ 'children' => [ 'CHILD_1_1', 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [] ],

                'CHILD_1_1' => [ 'children' => [ 'CHILD_1_2', 'CHILD_1_3', 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1' ] ],
                'CHILD_1_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_3' => [ 'children' => [ 'CHILD_1_4' ], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1', 'CHILD_1_3' ] ],

                'MAIN_PARENT_2' => [ 'children' => [ 'CHILD_2_1', 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [] ],

                'CHILD_2_1' => [ 'children' => [ 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2' ] ],
                'CHILD_2_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_3' => [ 'children' => [ 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1', 'CHILD_2_3' ] ],
            ],
            'D1' => [
                'IRRELEVANT' => [ 'children' => [], 'parents' => [] ],

                'MAIN_PARENT_1' => [ 'children' => ['CHILD_1_1', 'CHILD_1_2', 'CHILD_1_3'], 'parents' => [] ],
                'CHILD_1_1' => [ 'children' => [ 'CHILD_1_2', 'CHILD_1_3' ], 'parents' => [ 'MAIN_PARENT_1' ] ],
                'CHILD_1_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],
                'CHILD_1_3' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_1', 'CHILD_1_1' ] ],

            ],
            'D2' => [
                'MAIN_PARENT_2' => [ 'children' => [ 'CHILD_2_1', 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [] ],
                'CHILD_2_1' => [ 'children' => [ 'CHILD_2_2', 'CHILD_2_3', 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2' ] ],
                'CHILD_2_2' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_3' => [ 'children' => [ 'CHILD_2_4' ], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1' ] ],
                'CHILD_2_4' => [ 'children' => [], 'parents' => [ 'MAIN_PARENT_2', 'CHILD_2_1', 'CHILD_2_3' ] ],
                'CHILD_2_5' => [ 'children' => [], 'parents' => [] ],
            ]
        ];

    }

    protected function tearDown(): void
    {
        $this->artisan('migrate:fresh');   
        parent::tearDown();
    }

    public function testSortV2(): void
    {

        $this->artisan('prefix:sort-v2');

        foreach($this->prefix_results as $asn_label => $asn_set) {
            foreach($asn_set as $prefix_label => $information) {

                // Retrieve prefix in question
                $prefix = $this->prefixes[$asn_label][$prefix_label];

                // Select supernets for this prefix
                $supernets = Supernets::where('subnet_id', '=', $prefix->id)->get();

                // Get subnets
                $subnets = Supernets::where('supernet_id', '=', $prefix->id)->get();

                // Confirm that the amount of supernets and subnets match
                $this->assertCount(sizeof($information['parents']), $supernets, "The number of supernets for $asn_label -> $prefix_label is wrong.");
                $this->assertCount(sizeof($information['children']), $subnets, "The number of subnets for $asn_label -> $prefix_label is wrong.");

                // Confirm that each piece of information exists within the supernets and subnets
                foreach(['children', 'parents'] as $lineage) {

                    foreach($information[$lineage] as $subprefix_label) {

                        // Find it
                        $found = $lineage === 'children' ? $subnets->where('subnet_id', '=', $this->prefixes[$asn_label][$subprefix_label]->id) : $supernets->where('supernet_id', '=', $this->prefixes[$asn_label][$subprefix_label]->id) ;

                        $this->assertTrue(sizeof($found) === 1 , "The $asn_label -> $prefix_label does not have the $lineage relation to $subprefix_label as we'd expect.");
                    }

                }

            }
        }
    }

    public function testSortV2Full(): void
    {

        $this->artisan('prefix:sort-v2 --full-sort=true');
        $this->artisan('prefix:sort-v2 --full-sort=true');

        foreach($this->prefix_results as $asn_label => $asn_set) {
            foreach($asn_set as $prefix_label => $information) {

                // Retrieve prefix in question
                $prefix = $this->prefixes[$asn_label][$prefix_label];

                // Select supernets for this prefix
                $supernets = Supernets::where('subnet_id', '=', $prefix->id)->get();

                // Get subnets
                $subnets = Supernets::where('supernet_id', '=', $prefix->id)->get();

                // Confirm that the amount of supernets and subnets match
                $this->assertCount(sizeof($information['parents']), $supernets, "The number of supernets for $asn_label -> $prefix_label is wrong.");
                $this->assertCount(sizeof($information['children']), $subnets, "The number of subnets for $asn_label -> $prefix_label is wrong.");

                // Confirm that each piece of information exists within the supernets and subnets
                foreach(['children', 'parents'] as $lineage) {

                    foreach($information[$lineage] as $subprefix_label) {

                        // Find it
                        $found = $lineage === 'children' ? $subnets->where('subnet_id', '=', $this->prefixes[$asn_label][$subprefix_label]->id) : $supernets->where('supernet_id', '=', $this->prefixes[$asn_label][$subprefix_label]->id) ;

                        $this->assertTrue(sizeof($found) === 1 , "The $asn_label -> $prefix_label does not have the $lineage relation to $subprefix_label as we'd expect.");
                    }

                }

            }
        }
    }


}
