<?php

namespace App\Modules\Network\Commands;

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

use Symfony\Component\Process\Process;

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

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Pushes profiles to the mitigation system.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $mark_as_sent_group  = [];
        $notification = [];
        $crusher_notification = ["*MITIGATION SUBMISSION STATEMENT*", "Quantitative summary of prefix adjustments to the mitigation system (Not happening for now)"];
        $ruleset_addresses = [];
        $sensitivity_addresses = [];

        // Reserve a reusable anonymous function for crushing addresses properly...
        $crusher = function($_crushable) {
            // Crush the category
            $crusher_process = new Process(['python3', './app/Bin/SubnetCrusher.py']);
            $crusher_process->setInput(implode(' ', $_crushable));
            $crusher_process->run();
                 
            if($crusher_process->isSuccessful()) {
                // Get and process the output into an array.
                $prefixes = [];
                preg_match_all('/((?:(?:(?:[A-Fa-f0-9]{1,4}:{1,2}){0,8})|(?:(?:\d{1,3}\.?)){4})\/\d{1,3})/', $crusher_process->getOutput(), $prefixes);

                return $prefixes[0];
            }

            return [];
        };

        try {
            $sensitivities = MitigationSensitivities::select('id', 'system_identifier')->get()->keyBy('id');
            $rulesets = MitigationRulesets::select('id', 'system_identifier')->get()->keyBy('id');
            $organisations = Organisation::select('id', 'name')->get()->keyBy('id');

            foreach($rulesets as $ruleset) {
                $ruleset_addresses[$ruleset->system_identifier] = [];
            }

            foreach($sensitivities as $sensitivity) {
                $sensitivity_addresses[$sensitivity->system_identifier] = [];
            }

            $ruleset_addresses["NONE"] = [];
            $sensitivity_addresses["NONE"] = [];

            PrefixMitigation::leftJoin('prefixes', 'prefix_id', '=', 'prefixes.id')->where([['sent', '=', '0'],['in_sync', '=', '0']])->chunk(env('CHUNK_SIZE'), function($network_chunk) use($sensitivities, $rulesets, $organisations, &$ruleset_addresses, &$sensitivity_addresses, &$mark_as_sent, &$notification) {
                foreach($network_chunk as $network) {
                    $piece = "*MITIGATION PROFILE CHANGE REQUEST*\n";
                    $piece .= ">*PREFIX*: $network->address/$network->mask\n";
                    $piece .= ">*ORGANISATION:* {$organisations[$network->organisation_id]->name}\n";

                    $ruleset = [];

                    if($network->ruleset_ids != null) {
                        foreach(explode(',', $network->ruleset_ids) as $ruleset_id) {
                            // Queue up rulesets to send over to Corero
                            $ruleset_addresses[$rulesets[$ruleset_id]->system_identifier][] = "$network->address/$network->mask";
    
                            $ruleset[] = $rulesets[$ruleset_id]->system_identifier;
                        }
                    }
                    else {
                        $ruleset_addresses['NONE'][] = "$network->address/$network->mask";
                    }

                    $ruleset_string = !count($ruleset) ? 'NONE' : implode(', ', $ruleset);
                    $sensitivity_string = isset($sensitivities[$network->sensitivity_id]) ? $sensitivities[$network->sensitivity_id]->system_identifier : 'NONE';

                    if(isset($sensitivities[$network->sensitivity_id])) {
                        // Queue up sensitivities to send over to Corero
                        $sensitivity_addresses[$sensitivities[$network->sensitivity_id]->system_identifier][] = "$network->address/$network->mask";
                    }
                    else {
                        $sensitivity_addresses["NONE"][] = "$network->address/$network->mask";
                    }

                    $piece .= ">*RULESETS:* $ruleset_string\n";
                    $piece .= ">*SENSITIVITY:* $sensitivity_string\n";

                    $notification[] = $piece;

                    $mark_as_sent[] = $network->id;
                }
            });

            $crushed_ruleset_addresses = [];
            $crushed_sensitivity_addresses = [];

            // Next, go over each ruleset and sensitivity address group, then crush them.
            foreach($ruleset_addresses as $key => $ruleset_group) {
                $crushed_ruleset_addresses[$key] = $crusher($ruleset_group);
                $crusher_notification[] = ">*RULESET $key:* Crushed " . count($ruleset_addresses[$key]) . " prefixes to " . count($crushed_ruleset_addresses[$key]) . ' prefixes';
            }

            foreach($sensitivity_addresses as $key => $sensitivity_group) {
                $crushed_sensitivity_addresses[$key] = $crusher($sensitivity_group);
                $crusher_notification[] = ">*SENSITIVITY $key:* Crushed " . count($sensitivity_addresses[$key]) . " prefixes to " . count($crushed_sensitivity_addresses[$key]) . ' prefixes';
            }

            // We'd specifically iterate over the crushed and uncrushed shit to remove it where it's none.

            // We'd probably then specifically iterate over the crushed shit to insert it.

            if(count($notification)) {
                $this::notify(implode("\n\n", $notification));
                $this::notify(implode("\n", $crusher_notification));
            }
        }
        catch(Exception $ex) {}

        try {
            //PrefixMitigation::whereIn('id', $mark_as_sent)->update([ 'sent' => 1 ]);
        }
        catch(Exception $ex){
            $this::notify('FAILED TO MARK THE FOLLOWING NETWORK PROFILE REQUESTS AS SENT: ' . implode(', ', $mark_as_sent));
        }
        
        return 0;
    }
}