<?php

namespace App\Modules\Network\Commands;

use Carbon\Carbon;
use DB;

// Creatia Core
use App\Models\Asns;

// Module Specifics
use App\Modules\Network\Models\Prefixes;
use App\Modules\Network\Models\Supernets;
use App\Modules\Network\Helpers\ControllerCommons;

use Log;

class PrefixSort extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'prefix:sort';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Sorts prefixes based on supernetting and subnetting.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        // Create reusable function for calculating subnets and supernets - Since V4 and V6 will use the same function but we just want to keep them separate to avoid wasted calculations.
        function x($prefixes, $supernet_lookup, $asn) {
            // Iterate over each subnet, finding anything that belongs inside it
            foreach($prefixes->chunk(env('CHUNK_SIZE')) as $potential_subnet_chunk) {
                foreach($potential_subnet_chunk as $potential_subnet) {
                    $to_insert = [];

                    foreach($prefixes->where('mask', '<', $potential_subnet->mask)->chunk(env('CHUNK_SIZE')) as $supernet_chunk) {
                        foreach($supernet_chunk as $supernet) {                                
                            // If the subnet is in this supernet...
                            if(ControllerCommons::isValidSubnetInSupernet(
                            "$potential_subnet->address/$potential_subnet->mask",
                                "$supernet->address/$supernet->mask"
                            )) {
                                if(!isset($supernet_lookup["$supernet->id-$potential_subnet->id"])) {
                                    // It doesn't exist yet so we can insert it.
                                    $to_insert[] = [
                                        'created_at' => Carbon::now(),
                                        'updated_at' => Carbon::now(),
                                        'tenant_id' => $asn->organisation_id,
                                        'supernet_id' => $supernet->id,
                                        'subnet_id' => $potential_subnet->id
                                    ];

                                    Log::debug("Supernet $supernet->address/$supernet->mask contains $potential_subnet->address/$potential_subnet->mask");
                                }
                            }
                        }
                    }
    
                    DB::table('supernets')->insert($to_insert);
                }

            }
        }

        // Largest to smallest
        Asns::where('approved', '=', '1')->chunk(env('CHUNK_SIZE'), function($asns) {
            foreach($asns as $asn) {
                // Select every potential supernet... Split into two parts, the v4's and v6's to avoid wasted calculation.
                $v4_prefixes = Prefixes::select('id', 'address', 'mask')->where([['asn_id', '=', $asn->id], [DB::raw('family(address)'), '=', '4']])->orderBy('mask')->get();
                $v6_prefixes = Prefixes::select('id', 'address', 'mask')->where([['asn_id', '=', $asn->id], [DB::raw('family(address)'), '=', '6']])->orderBy('mask')->get();

                // Create a lookup of known associations
                $supernet_lookup = Supernets::select(DB::raw('concat(supernet_id, \'-\', subnet_id) as lookup'))->whereNotNull('supernet_id')->whereNotNull('subnet_id')->get()->keyBy('lookup');

                x($v4_prefixes, $supernet_lookup, $asn);
                x($v6_prefixes, $supernet_lookup, $asn);
            }
        });
    
        return 0;
    }
}