<?php

namespace App\Console\Commands\Network;

use App\Console\Commands\BaseCommand;
use Illuminate\Support\Facades\DB;

use App\Models\Prefixes;
use App\Models\PrefixInclusions;
use App\Models\Asns;

use App\Helpers\Utility\UtilPrefix;

class PrefixAssociate extends BaseCommand
{

    protected $signature = 'prefix:associate';

    protected $description = 'Associate known prefixes against known and approved ASNs, while associating communities and paths.';

    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 = [];
        $prefix_community_dictionary = [];

        foreach([...UtilPrefix::get_detailed(), ...UtilPrefix::get_detailed_v6()] 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];
            }

            $prefix_community_dictionary[$announced_prefix->prefix] = $announced_prefix->communities;
        }

        foreach(PrefixInclusions::leftJoin('asns', 'asn_id', '=', 'asns.id')->select('address', 'mask', 'asn')->get() as $included_prefix) {
            if(isset($originator_dictionary[$included_prefix->asn])) {
                $originator_dictionary[$included_prefix->asn][] = "$included_prefix->address/$included_prefix->mask";
            }
            else {
                $originator_dictionary[$included_prefix->asn] = [ "$included_prefix->address/$included_prefix->mask" ];
            }
        }

        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(config('creatia.constant.chunk_medium'), function($asns) use($originator_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) {

                    // 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] : []
                    ] 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;
    }
}
