<?php

namespace App\Console\Commands\Report;

use App\Models\Goliath\GoliathAttack;
use App\Models\Goliath\GoliathAttackCollected;
use App\Models\Goliath\ReportAwareness;
use App\Models\Prefixes;
use App\Models\ReportOrganisationAttribute;
use App\Repositories\ReportRepository;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class AttackTrackStart extends \App\Console\Commands\BaseCommand
{
    protected $signature = 'attack-track:start';

    protected $description = 'Select report singulars while they are ongoing then determine if they have reached a customers threshold and IF they have send a start notification';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $report_repository = new ReportRepository;

        $recently_attacked_organisations = GoliathAttack::join('goliath_attack_prefixes', 'goliath_attack_prefixes.attack_id', '=', 'goliath_attacks.id')
            ->join('prefixes', 'goliath_attack_prefixes.prefix_id', '=', 'prefixes.id')
            ->join('goliath_attack_collected', 'goliath_attacks.id', '=', 'goliath_attack_collected.attack_id')
            ->where('last_at', '>', Carbon::now()->subMinutes(20))
            ->distinct('organisation_id')
            ->get()
            ->pluck('organisation_id');

        $organisation_attributes = ReportOrganisationAttribute::whereIn('organisation_id', $recently_attacked_organisations)
            ->select('minimum_threshold_gbps', 'minimum_threshold_mpps', 'organisation_id')
            ->get()
            ->keyBy('organisation_id');

        $to_insert = [];

        $cached_list = Redis::get('report-singular-automation-list');

        if(!$cached_list) {
            return 0;
        }

        $all_attacks = collect(json_decode($cached_list, true)['data'])->flatMap(function ($attack) {
            return collect($attack['subtenants'])->map(function ($subtenant_id) use ($attack) {
                return array_merge($attack, ['subtenant_id' => $subtenant_id]);
            });
        })
        ->groupBy('subtenant_id')
        ->map(function ($attacks) {
            return $attacks->unique('report_id');
        });

    
        foreach($recently_attacked_organisations as $organisation) {

            if(!isset($all_attacks[$organisation]) || !isset($organisation_attributes[$organisation])) continue;

            $attacks = $all_attacks[$organisation];
            
            $attributes = $organisation_attributes[$organisation]->toArray();
            
            $aware = ReportAwareness::where('type', 'start')
                ->whereIn('report_id', $attacks->pluck('report_id'))
                ->select('report_id')
                ->get()
                ->keyBy('report_id')
                ->toArray();

            $collected = GoliathAttackCollected::whereIn('attack_id', $attacks->pluck('report_id'))->get()->keyBy('attack_id')->toArray();

            // We're using this to keep track of whether or not a prefix should even be considered in the first place.
            $valid_prefixes = Prefixes::getOrganisationPrefixes($organisation)->pluck('id');

            $now_aware_of = [];

            foreach($attacks as $attack) {

                if(isset($now_aware_of[$attack['report_id']])) continue;

                if(isset($aware[$attack['report_id']])) continue; // Exit if we're already aware of this attack in this context

                if(!isset($collected[$attack['report_id']])) continue; // Exit if we haven't collected this attack into the reporting system

                if(Carbon::parse($attack['finish_at'])->unix() < Carbon::now()->subMinutes(15)->unix()) continue; // Attack needs to have been in the last fifteen minutes

                // If the organisation has no real reason to ever be allowed access to the prefix, bail out
                if(Prefixes::whereRaw('family(address) = 4')->whereIn('id', $valid_prefixes)->where(DB::raw("CONCAT(address, '/', mask)::cidr"), '>>=', (str_replace('::ffff:', '', $attack['destination']) . '/32'))->count() === 0) {
                    $this->log_error("An inappropriate report was going to be sent to an inappropriate organisation.", [
                        'organisation' => $organisation,
                        'attack' => $attack
                    ]);
                    continue;
                };

                $data = $report_repository->get_attack_start_sizes(
                    $attack['destination'],
                    Carbon::parse($attack['start_at']),
                    Carbon::now()->subMinutes(2)->timestamp,
                    $attributes['minimum_threshold_gbps'],
                    $attributes['minimum_threshold_mpps']
                );

                if($data === null || !sizeof($data['data'])) continue; // If we get no data, don't do anything

                $this->log_event('Attack start tracking', [ 'attack' => $attack, 'data' => $data ]);

                if($data['data'][0]['MaxGbps'] < $attributes['minimum_threshold_gbps'] && $data['data'][0]['MaxMpps'] < $attributes['minimum_threshold_mpps']) continue; // Attack has to be big enough

                $now_aware_of[$attack['report_id']] = true;

                $to_insert[] = [
                    'created_at'      => Carbon::now(),
                    'updated_at'      => Carbon::now(),
                    'detected_at'     => Carbon::parse($attack['start_at']),
                    'type'            => 'start',
                    'status'          => 'pending',
                    'report_id'       => $attack['report_id'],
                    'organisation_id' => $organisation,
                    'gbps'            => $data['data'][0]['MaxGbps'],
                    'mpps'            => $data['data'][0]['MaxMpps'],
                    'address'         => str_replace('::ffff:', '', $attack['destination'])
                ];
            }
        }

        if($to_insert !== []) {
            ReportAwareness::insert($to_insert);
        }

        return 0;
    }
}