<?php

namespace App\Console\Commands\Report;

use App\Helpers\ClickhouseHandler;
use App\Models\Goliath\ReportAwareness;
use App\Models\ReportOrganisationAttribute;
use Carbon\Carbon;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Select carpets while they are ongoing then determine if they have reached a customers threshold and IF they have sent a start notification';

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

    public function getOngoingAttacks($chh) {

        // This will typically be quite small and quick to iterate over as we're only selected things that have started in the last half hour. @TO-DO: Refactor to use the complete table instead.
        return collect($chh->select("SELECT
                carpet_id,
                creatia_organisation_id as organisation_id,
                destination_parent,
                subtractMinutes(MIN(start_at), 2)::int AS measure_from,
                addMinutes(MAX(start_at), 2)::int AS measure_to,
                start_at AS started_at
            FROM (
                SELECT
                    report_carpet.carpet_id AS carpet_id,
                    start_at AS start_at,
                    report_carpet.destination_parent AS destination_parent,
                    report_carpet.creatia_organisation_id AS creatia_organisation_id
                FROM
                    report_carpet
                LEFT JOIN
                    report_carpet_singular ON report_carpet.carpet_id = report_carpet_singular.carpet_id
                LEFT JOIN
                    report_singular FINAL ON report_singular.singular_id = report_carpet_singular.singular_id
                WHERE
                    start_at::int > now() - 1800 AND
                    report_carpet.carpet_id NOT IN (
                        SELECT carpet_id 
                        FROM report_carpet_complete 
                        WHERE carpet_id != 0
                    )
            ) as A
            GROUP BY carpet_id, creatia_organisation_id, destination_parent, start_at
        ")->get());
    }

    public function getAttackData($chh, $from, $to, $dst, $min_gbps, $min_mpps) {

        return $chh->selectUnpaged("SELECT
            MAX(Gbps) as MaxGbps,
            MAX(Mpps) as MaxMpps 
            FROM (
                SELECT
                    toStartOfInterval(TimeReceived, INTERVAL 1 SECOND) AS TimeReceived,
                    SUM(Bytes * SamplingRate * 8) / 1000 / 1000 / 1000 AS Gbps,
                    SUM(Packets * SamplingRate) / 1000000 AS Mpps
                FROM
                    flows 
                WHERE 
                    TRIM(LEADING '::ffff:' FROM DstAddr::String) != '' AND TimeReceived > {p0:integer} AND TimeReceived < {p1:integer} AND isIPAddressInRange(TRIM(LEADING '::ffff:' FROM DstAddr::String), {p2:String}) AND InIfBoundary = 'external' 
                GROUP BY TimeReceived
                ORDER BY TimeReceived ASC
            )
            HAVING
                MaxGbps >= {p3:integer} AND MaxMpps >= {p4:float}", [ $from, $to, $dst, $min_gbps, $min_mpps ]
        )->get(true, true);

    }

    public function iterateOverAttacks($chh_reporting, $chh_akvorado, $ongoing_source, $attack_source) {

        $ongoing_attacks = $ongoing_source($chh_reporting);

        // We should select a report awareness list to determine whether or not we need track something...
        $aware = ReportAwareness::where('type', '=', 'carpet-start')->whereIn('report_id', $ongoing_attacks->pluck('carpet_id'))->select('report_id')->get()->keyBy('report_id');

        // Get all organisation attributes, we'll use this for determining things that need to happen
        $organisation_attributes = ReportOrganisationAttribute::whereIn('organisation_id', $ongoing_attacks->pluck('organisation_id'))->select(
            'minimum_threshold_gbps',
            'minimum_threshold_mpps',
            'organisation_id'
        )->get()->keyBy('organisation_id');

        $to_insert = [];

        $now = Carbon::now();

        foreach($ongoing_attacks as $x) {

            // If we're not aware of this attack...
            if(!isset($aware[$x['carpet_id']]) && isset($organisation_attributes[$x['organisation_id']])) {
                $attributes = $organisation_attributes[$x['organisation_id']]->toArray();

                //Select data from our measuring point...
                $data = $attack_source($chh_akvorado, $x['measure_from'], $x['measure_to'], $x['destination_parent'], $attributes['minimum_threshold_gbps'], $attributes['minimum_threshold_mpps']);

                if($data !== null && sizeof($data['data'])) {

                    $data = $data['data'];

                    // We only ever want to queue these up IF they were from the past half hour. This will prevent us from destroying rate limits should there ever be a cessation.

                    if($x['started_at'] > (time()-1800)) {
                        $to_insert[] = [
                            'created_at'        => $now,
                            'updated_at'        => $now,
                            'detected_at'       => Carbon::parse($x['started_at']),
                            'type'              => 'carpet-start',
                            'status'            => 'pending',
                            'report_id'         => $x['carpet_id'],
                            'organisation_id'   => $x['organisation_id'],
                            'gbps'              => $data[0]['MaxGbps'],
                            'mpps'              => $data[0]['MaxMpps'],
                            'address'           => ltrim($x['destination_addr'], '::ffff:')
                        ];
                    }
                }
            }
        }

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

    }

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

        $chh_reporting = new ClickhouseHandler(
            config('creatia.reporting.db'),
            config('creatia.reporting.host'),
            config('creatia.reporting.port'),
            config('creatia.reporting.user'),
            config('creatia.reporting.pass'),
            null,
            false
        );

        $chh_akvorado = new ClickhouseHandler(
            'default',
            null,
            null,
            null,
            null,
            'http://10.100.18.32:9000/'
        );

        $this->iterateOverAttacks($chh_reporting, $chh_akvorado, fn($a) => $this->getOngoingAttacks($a), fn($a, $b, $c, $d, $e, $f) => $this->getAttackData($a, $b, $c, $d, $e, $f));

        return 0;
    }
}