<?php

namespace App\Modules\Network\Commands;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Log;

use App\Modules\Network\Models\Prefixes;
use App\Modules\Network\Models\PrefixInclusions;
use App\Modules\Network\Models\Supernets;

use App\Models\Asns;

use App\Helpers\Utility\RouteList;
use App\Modules\Network\Models\PrefixCommunity;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Associate known prefixes against known and approved ASNs, while associating communities and paths.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        // Get a list of prefixes that belong to a given ASN - We're essentially creating an ASN to prefix dictionary...
        $originator_dictionary = [];
        $owner_dictionary = [];
        $prefix_community_dictionary = [];

        foreach(collect(Http::get("http://54.215.89.235/all.active.detailed.json")->json()) as $announced_prefix) {
            if(isset($originator_dictionary[$announced_prefix['asn_originator']])) {
                $originator_dictionary[$announced_prefix['asn_originator']][] = $announced_prefix['prefix'];
            }
            else {
                $originator_dictionary[$announced_prefix['asn_originator']] = [$announced_prefix['prefix']];
            }

            if(isset($owner_dictionary[$announced_prefix['asn_owner']])) {
                $owner_dictionary[$announced_prefix['asn_owner']][] = $announced_prefix['prefix'];
            }
            else {
                $owner_dictionary[$announced_prefix['asn_owner']] = [$announced_prefix['prefix']];
            }

            $prefix_community_dictionary[$announced_prefix['prefix']] = $announced_prefix['communities'];
        }

        if(sizeof($prefix_community_dictionary) === 0) {
            return 0;
        }

        // Iterate of each individual ASN and begin associating as needed, marking things as announced where possible.
        Asns::where('approved', '=', '1')->chunk(env('CHUNK_SIZE'), function($asns) use($originator_dictionary, $owner_dictionary) {

            // Iterate over every individual ASN - Keeps things lighter if we do things per 
            foreach($asns as $asn) {
                DB::transaction(function() use($asn, $originator_dictionary, $owner_dictionary) {

                    // Do a pass with the originators first, then the owners - This ensures no conflict in this order.
                    foreach([
                        isset($originator_dictionary[$asn->asn]) ? $originator_dictionary[$asn->asn] : [],
                        isset($owner_dictionary[$asn->asn]) ? $owner_dictionary[$asn->asn] : []
                    ] as $dictionary) {
                        // Only proceed if we've got anything...
                        if(sizeof($dictionary) !== 0) {

                            // Get the IDs of every single prefix in this chunk
                            $relevant_prefix_ids = Prefixes::whereIn(DB::raw('concat("address", \'/\', "mask")'), $dictionary)->whereNull('asn_id')->get()->pluck('id');

                            if($relevant_prefix_ids->count()) {
                                // Swap each of these IDs over only if the ASN ID doesn't already line up...
                                Prefixes::whereIn('id', $relevant_prefix_ids)->whereNull('asn_id')->update([
                                    'asn_id' => $asn->id,
                                    'organisation_id' => $asn->organisation_id,
                                    'setup_required' => 1,
                                    'name' => null
                                ]);
                            }
                        }
                    }

                    return true;
                });
            }
        });
        
        return 0;
    }
}
