<?php

namespace App\Console\Commands\Report;

use App\Helpers\ClickhouseHandler;
use App\Models\Prefixes;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class ReportAttackCarpetNew extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'report-attack:carpet-new';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Correlates attack triggers and general attacks and builds an enriched object.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {

        // Get list of relevant attack records
        $chh = new ClickhouseHandler(
            config('creatia.reporting.db'),
            config('creatia.reporting.host'),
            config('creatia.reporting.port'),
            config('creatia.reporting.user'),
            config('creatia.reporting.pass'),
            null,
            false
        );

        $attack_records = collect($chh->selectUnpaged("SELECT
            MIN(start_at) as start_at,
            MAX(finish_at) as finish_at,
            destination_parent,
            destination_addr,
            destination_type,
            creatia_organisation_id,
            id
            FROM (
                SELECT
                    report_singular.singular_id AS id,
                    start_at::int as start_at,
                    finish_at::int as finish_at,
                    destination_parent as destination_parent,
                    destination_addr as destination_addr,
                    destination_addr_type as destination_type,
                    creatia_organisation_id
                FROM
                    report_singular
                LEFT JOIN
                    report_carpet_singular ON report_carpet_singular.singular_id = report_singular.singular_id
                INNER JOIN
                    report_singular_complete ON report_singular.singular_id = report_singular_complete.singular_id
                WHERE
                    finish_at > :time AND
                    report_carpet_singular.singular_id  = 0
            )
            GROUP BY
                id, destination_parent, destination_addr, destination_type, creatia_organisation_id
        ", [
            'time' => Carbon::now()->subHour()->timestamp
        ])->get());

        $ctr = 0;
        $ttl = $attack_records->groupBy('destination_parent')->count();

        $blankets = [];

        foreach($attack_records->groupBy('destination_parent') as $x24) {

            if(count($x24->pluck('destination_addr')) > 9) {


                // Build up histogram
                $histogram_24 = [];
                $index_min = 1000000000000000;
                $index_max = 0;

                foreach($x24 as $x32) {

                    if($index_min > $x32['start_at']) {
                        $index_min = $x32['start_at'];
                    }

                    if($index_max < $x32['finish_at']) {
                        $index_max = $x32['finish_at'];
                    }
    
                    for($i = $x32['start_at']; $i <= $x32['finish_at'] + 600; ++$i) {
                        if(isset($histogram_24[$i]['prefix'])) {
                            $histogram_24[$i]['prefix'][] = $x32['destination_addr'];
                            $histogram_24[$i]['id'][] = $x32['id'];
                        }
                        else {
                            $histogram_24[$i]['prefix'] = [ $x32['destination_addr'] ];
                            $histogram_24[$i]['id'] = [ $x32['id'] ];
                        }

                    }
                }

                // Associate carpets from the histogram
                $blanket_prefixes = [];
                $blanket_identities = [];

                $cur_start = 0;

                for ($i = $index_min; $i <= $index_max + 1; ++$i) {
                    if(isset($histogram_24[$i])) {

                        if(sizeof($histogram_24[$i]['prefix']) !== 0) { 

                            if($cur_start === 0) {
                                $cur_start = $i;
                            }

                            $blanket_prefixes = array_unique(array_merge($blanket_prefixes, $histogram_24[$i]['prefix']));
                            $blanket_identities = array_unique(array_merge($blanket_identities, $histogram_24[$i]['id']));
                        }
                    }
                    else {
                        if($blanket_prefixes !== []) {

                            \Log::warning(count($blanket_prefixes));

                            if(count($blanket_prefixes) > 9) {

                                // Determine whether or not the /24 is presently owned by someone - We want to select the nearest owner of a given /24
                                $owners = Prefixes::where([
                                        [ 'created_source', '=', 'prefix-list-uncrushed' ],
                                        [ DB::raw('family(address)'), '=', 4 ],
                                        [ 'mask', '<=', 24 ],
                                        [ DB::raw('concat(address, \'/\', mask)::cidr'), '>>=', ltrim($x24[0]['destination_parent'], '::ffff:') . '/24' ]
                                    ])
                                    ->select('organisation_id')
                                    ->orderBy('mask', 'desc')
                                    ->get()
                                    ->unique()
                                    ->pluck('organisation_id');

                                if(sizeof($owners) === 0) {
                                        $blankets[] = [
                                            'parent' => $x24[0]['destination_parent'],
                                            'family' => $x24[0]['destination_type'],
                                            'primary_identity' =>  100 . $blanket_identities[0], 
                                            'identities' => $blanket_identities,
                                            'start_at' => $cur_start,
                                            'finish_at' => $i,
                                            'organisation_id' => null
                                        ];
                                }
                                else {
                                    foreach($owners as $owner) {
                                        $blankets[] = [
                                            'parent' => $x24[0]['destination_parent'],
                                            'family' => $x24[0]['destination_type'],
                                            // We need to pad it this way, otherwise we could end up with rare collisions between a concatenation of an organisation and attack. I.E 181 . 1234, 18 . 11234
                                            // Current padding helps - 18 . 00 . 1234, 180 . 00 . 1234 - The two zeroes can never occur before an attack identifier, and even if the owner ended with zeroes there is still two zeros - Two last zeros will always extend it
                                            'primary_identity' => $owner . 00 . $blanket_identities[0], 
                                            'identities' => $blanket_identities,
                                            'start_at' => $cur_start,
                                            'finish_at' => $i,
                                            'organisation_id' => $owner
                                        ];
                                    }
                                }


                            }

                            $blanket_prefixes = [];
                            $blanket_identities = [];

                            $cur_start = 0;
                        }
                    }
                }
            }

            $ctr++;
            echo (($ctr / $ttl) * 100) . "%\n";
        }

        $to_insert_associations = [];
        $to_insert_carpets = [];

        foreach($blankets as $blanket) {

            $to_insert_carpets[] = [
                'carpet_id'                 => $blanket['primary_identity'],
                
                'destination_parent'        => $blanket['parent'],
                'destination_parent_type'   => $blanket['family'],
                'destination_size'          => 24,

                'creatia_organisation_id'   => $blanket['organisation_id'],
            ];

            foreach($blanket['identities'] as $identity) {
                $to_insert_associations[] = [
                    'singular_id' => $identity,
                    'carpet_id' => $blanket['primary_identity']
                ];
            }
        }

        if(sizeof($to_insert_carpets)) {

            $chh->insert('report_carpet', function($r) {    
                return true;
            }, $to_insert_carpets);
    
            $chh->insert('report_carpet_singular', function($r) {    
                return true;
            }, $to_insert_associations);
        }

        return 0;
    }
}