<?php

namespace App\Modules\Network\Commands;

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

use App\Modules\Network\Models\Mitigation\PrefixMitigation;
use App\Modules\Network\Models\Mitigation\MitigationSensitivities;
use App\Modules\Network\Helpers\Corero;

class MitigationSensitivityPull extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'mitigation:sensitivity-pull';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Pulls sensitivities from the mitigation system.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        try {            
            $corero = new Corero(env('CORERO_ENDPOINT'), env('CORERO_API_VERSION'), env('CORERO_USERNAME'), env('CORERO_PASSWORD'));

            // Worst case complexity of the below is ceiling(numOfSensitivities / chunkSize) * ceiling(numOfPrefixes / chunkSize)^2 - Not great but realistically laid out to avoid memory limits being exceeded.
            // We need to iterate over EVERY sensitivity stored in our database. Probably won't ever need to chunk, but never say never.
            MitigationSensitivities::chunk(env('CHUNK_SIZE'), function($sensitivity_chunk) use($corero) {            
                // Grab prefixes for this sensitivity.
                foreach($sensitivity_chunk as $sensitivity) {
                    $unchunked_prefixes = json_decode($corero->get_address_group_list($sensitivity->system_identifier)->body())->items;

                    // Split the values into chunks so we don't end up with 500+ values being inserted into the database at any given time - Can handle higher but lets keep it reasonable.
                    $chunked_prefixes = array_chunk($unchunked_prefixes, env('CHUNK_SIZE'));
    
                    // Iterate over each chunk and it's corresponding prefixes.
                    foreach($chunked_prefixes as $prefixes) {
                        $networks_to_fetch = [];
    
                        foreach($prefixes as $prefix) {
                            if(!str_contains($prefix->address, '/')) {
                                $networks_to_fetch[] = $prefix->address . '/32';
                            }
                            else {
                                $networks_to_fetch[] = $prefix->address;
                            }
                        }

                        if($networks_to_fetch != []) {                        
                            // Tasty as this only executes once up to every env('CHUNK_SIZE') prefixes per sensitivity.
                            PrefixMitigation::leftJoin('prefixes', 'prefix_id', '=', 'prefixes.id')->whereIn(DB::raw("CONCAT(address,'/',mask)"), $networks_to_fetch)
                                ->whereNull('prefixes.deleted_at')
                                ->whereRaw('(in_sync = 1)')
                                ->whereRaw('(synced_at >= prefix_mitigation.updated_at OR synced_at is null)')
                                ->whereRaw('(sent is null OR sent = 1)')
                                ->update([
                                    'sensitivity_id' => $sensitivity->id,
                                    'sent' => null,
                                    'synced_at' => Carbon::now(),
                                    'last_source' => 'corero'
                                ]);
                        }
                    }
                }
            });
        }
        catch(Exception $ex) {

        }
        
        return 0;
    }
}