<?php

namespace App\Modules\Network\Commands;

use Symfony\Component\Process\Process;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Log;

use App\Modules\Network\Models\Prefixes;
use App\Modules\Network\Helpers\CoreroSSP;

use App\Models\Asns;
use App\Modules\Network\Models\Mitigation\MitigationOrganisationAttributes;

use App\Helpers\Utility\RouteList;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Pushes prefixes across to SSP.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $corero_ssp = new CoreroSSP(env('CORERO_SSP_ENDPOINT'), env('CORERO_SSP_API_VERSION'), env('CORERO_SSP_USERNAME'), env('CORERO_SSP_PASSWORD'));

        Asns::where('approved', '=', '1')->chunk(env('CHUNK_SIZE'), function($asns) use($corero_ssp) {
            // Get all related organisation SSP identifiers
            $ssp_ids = MitigationOrganisationAttributes::select('ssp_id', 'organisation_id')->whereIn('organisation_id', $asns->pluck('organisation_id'))->get()->keyBy('organisation_id');

            foreach($asns as $asn) {
                $ssp_id = isset($ssp_ids[$asn->organisation_id]) ? $ssp_ids[$asn->organisation_id]->ssp_id : false;
                $ssp_network_ids = [];

                if($ssp_id) {
                    $ssp_to_insert = [];

                    $ssp_network_ids = $corero_ssp->get_prefixes($ssp_ids[$asn->organisation_id]->ssp_id)->keyBy('ip') ?? [];
                    $ssp_network_ips = $ssp_network_ids->pluck('ip')->toArray();

                    // Find things we need to insert into Corero. 
                    Prefixes::select(DB::raw('concat("address", \'/\', "mask") as cidr'))->where([['asn_id', '=', $asn->id],['organisation_id', '=', $asn->organisation_id]])->chunk(env('CHUNK_SIZE'), function($network_chunk) use($ssp_network_ids, &$ssp_to_insert) {
                        foreach($network_chunk as $network) {
                            if(!isset($ssp_network_ids[$network['cidr']])) {
                                $ssp_to_insert[] = $network['cidr'];
                            }
                        }
                    });

                    // Crush before we insert
                    $crusher_process = new Process(['python3', './app/Bin/SubnetCrusher.py']);
                    $crusher_process->setInput(implode(' ', $ssp_to_insert));
                    $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);

                        Log::debug("NUMBER OF PREFIXES ATTEMPTING TO PUSH TO SSP: " . count($prefixes[1]));

                        $num_pushed = 0;

                        foreach($prefixes[1] as $insertable) {
                            try {
                                // Check if this is a subnet of anything already in Corero, if so, discard.
                                if(RouteList::ip_in_blocks($insertable, $ssp_network_ips)) {
                                    Log::debug('SUPERNET EXISTS AND THEREFORE DISCARDING - SUBNET (Creatia): ' . $insertable);
                                }
                                else {
                                    // Check if this contains subnets in Corero, if so, delete these subnets before going postal.
                                    foreach($ssp_network_ips as $ssp_subnet) {
                                        if(RouteList::ip_in_block($ssp_subnet, $insertable) && $ssp_subnet != $insertable) {
                                            Log::debug('OVERLAP DETECTED - DELETE (SSP): ' . $ssp_subnet . ', WILL POST (Creatia): ' . $insertable);

                                            Log::debug($corero_ssp->delete_prefix($ssp_id, $ssp_subnet));
                                        }
                                    }

                                    if(array_search($insertable, $ssp_network_ips) === false) {
                                        // With everything out of the way, it's now safe to fling it in.
                                        Log::debug('VALID INSERT DETECTED - POST (Creatia): ' . $insertable);
                                        ++$num_pushed;
                                        Log::debug($corero_ssp->post_prefix($ssp_id, $insertable, $insertable));
                                    }
                                    else {
                                        Log::debug('DUPLICATE INSERT DETECTED, DISCARDING - ' . $insertable);
                                    }
                                    
                                }
                            }
                            catch(\Exception $th) {
                                Log::debug("FAILED PUSH ATTEMPT");
                                Log::debug($th);
                            }
                        }

                        Log::debug("NUMBER OF PREFIXES ACTUALLY PUSHED TO SSP: $num_pushed\n\n");
                    }
                }
            }
        });
        
        return 0;
    }
}