<?php

namespace App\Console\Commands\Network;

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

use App\Models\Prefixes;

use App\Helpers\Utility\UtilPrefix;
use Illuminate\Support\Facades\Log;

class PrefixUpdatePeer extends BaseCommand
{

    protected $signature = 'prefix:update-peer';

    protected $description = 'Update the peers associated with a given prefix.';

    public function handle()
    {
        foreach(UtilPrefix::get_detailed()->chunk(config('creatia.constant.chunk_large')) as $prefix_chunk) {

            $to_insert = [];
            $to_update = [];

            // Track prefixes to be aware of for this chunk
            $known_prefixes = Prefixes::leftJoin('prefix_asn_peers', 'prefix_asn_peers.prefix_id', '=', 'prefixes.id')
                ->select('id', 'peer', 'owner', DB::raw('concat("address", \'/\', "mask") as cidr'))
                ->whereIn(DB::raw('concat("address", \'/\', "mask")'), $prefix_chunk->pluck('prefix'))
                ->get()
                ->keyBy('cidr');

            foreach($prefix_chunk as $prefix) {

                // If something is set, and the ASNs do not line up...
                
                if(isset($known_prefixes[$prefix->prefix])) {
                    if(isset($known_prefixes[$prefix->prefix]->peer) && ($known_prefixes[$prefix->prefix]->peer != $prefix->asn_originator || $known_prefixes[$prefix->prefix]->owner != $prefix->asn_owner)) {

                        $to_update[] = [
                            'prefix_id' => $known_prefixes[$prefix->prefix]->id,
                            'peer'      => $prefix->asn_originator,
                            'owner'     => $prefix->asn_owner
                        ];
                    }
                    elseif(!isset($known_prefixes[$prefix->prefix]->peer)) {
                        $to_insert[] = [
                            'prefix_id'     => $known_prefixes[$prefix->prefix]->id,
                            'peer'          => $prefix->asn_originator,
                            'owner'         => $prefix->asn_owner
                        ];
                    }
                }
                
            }

            // Add in networks that need to be inserted.
            if($to_insert !== []) {
                foreach(array_chunk($to_insert, config('creatia.constant.chunk_small')) as $insertion_chunk) {
                    DB::table('prefix_asn_peers')->insert($insertion_chunk);
                }

                Log::info('Inserted ' . sizeof($to_insert) . ' prefix_asn_peers');
            }

            if($to_update !== []) {
                // One set-based statement per chunk rather than one UPDATE round-trip per row.
                foreach(array_chunk($to_update, config('creatia.constant.chunk_small')) as $update_chunk) {
                    $bindings = [];

                    foreach($update_chunk as $x) {
                        array_push($bindings, $x['prefix_id'], $x['peer'], $x['owner']);
                    }

                    DB::update(
                        'UPDATE prefix_asn_peers AS p SET peer = v.peer, owner = v.owner ' .
                        'FROM (VALUES ' . implode(', ', array_fill(0, sizeof($update_chunk), '(?::bigint, ?::bigint, ?::bigint)')) . ') AS v(prefix_id, peer, owner) ' .
                        'WHERE p.prefix_id = v.prefix_id',
                        $bindings
                    );
                }

                Log::info('Updated ' . sizeof($to_update) . ' prefix_asn_peers');
            }
        };

        return 0;
    }
}
