<?php

namespace App\Console\Commands\Report;

use App\Models\Goliath\ReportAwareness;
use App\Models\Organisation;
use App\Models\Prefixes;
use App\Models\Webhook;
use App\Models\WebhookMessage;
use App\Models\WebhookMessageStatus;
use App\Models\WebhookUsageType;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Queue up attack start or completion information for sending via webhook...';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {        
        // We need to segregate things based upon ASN to keep things memory friendly and parameter size friendly
        Organisation::join('report_organisation_attributes', 'organisation_id', '=', 'organisations.id')->where('send_webhook', '=', 1)->select(
            'organisations.id',
            'send_start',
            'send_finish',
            'send_webhook',
            'name'
        )->each(function($organisation) {

            $hooks = Webhook::where('organisation_id', '=', $organisation->id)->where('usage_type', '=', WebhookUsageType::Reporting->value)->select('id', 'hook', 'hook_type')->get()->toArray();

            $pending = [];

            // We double up here so when we eventually add things like email notifications we can just add an || in as we'll keep the hook and email blocks separate.
            if(sizeof($hooks) !== 0) {
                $pending = ReportAwareness::where([['organisation_id', '=', $organisation->id], ['status', '=', 'pending'], ['created_at', '>', Carbon::now()->subMinutes(20)]])->orderBy('detected_at', 'asc')->get()->toArray();
            }

            // Only proceed when we've got something pending
            if(sizeof($pending) !== 0) {

                // Webhook block
                if(sizeof($hooks) !== 0 && $organisation->send_webhook == true) {

                    foreach($pending as $p) {

                        // Check if the organisation has ownership of the prefix - They should, but we need to be as defensive as possible with this...
                        if(Prefixes::getOrganisationOwnsPrefixes($organisation->id, [ [ 'address' => $p['address'], 'time' => Carbon::now() ] ])[0] === false) {

                            $this->log_warning('Bad queue attempt for attack start/finish', [
                                'organisation' => $organisation->id,
                                'attack' => $p
                            ]);

                            continue;
                        }

                        // Wrap this all in a transaction since it'll need to execute atomically
                        DB::transaction(function() use($organisation, $p, $hooks) {

                                if(($p['type'] == 'start' && $organisation->send_start) || ($p['type'] == 'finish' && $organisation->send_finish)) {
                                // Send it along each valid webhook
                                foreach($hooks as $hook) {

                                    // Create a reference ID
                                    $ref_id =  strtoupper(str_pad(dechex($p['organisation_id']), 6, '0', STR_PAD_LEFT)) . '-' . str_pad($p['report_id'], 12, '0', STR_PAD_LEFT);

                                    // Build up notification for shunting
                                    $notification = $p['type'] == 'start' ? [
                                        'An attack has met the configured threshold to trigger an alert.',
                                        'Destination: ' . $p['address'] . '/32',
                                        'Started At: ' . Carbon::parse($p['detected_at'])->format('Y-m-d H:i:s') . ' UTC',
                                        'Gbps: ' . round($p['gbps'], 2),
                                        'Mpps: ' . round($p['mpps'], 2),
                                        "Reference: S-$ref_id"
                                    ] : [
                                        'An attack has meeting the threshold has concluded. A report will soon be generated if the attack was large enough.',
                                        'Destination: ' . $p['address'] . '/32',
                                        'Concluded At: ' . Carbon::parse($p['detected_at'])->format('Y-m-d H:i:s') . ' UTC',
                                        'Gbps: ' . round($p['gbps'], 2),
                                        'Mpps: ' . round($p['mpps'], 2),
                                        "Reference: F-$ref_id"
                                    ];

                                    (new WebhookMessage([
                                        'key'               => 'as-' . $p['id'],
                                        'message_type'      => WebhookUsageType::Reporting->value,
                                        'payload'           => $notification,
                                        'webhook_id'        => $hook['id'],
                                        'status'            => WebhookMessageStatus::Pending->value,
                                        'sent_at'           => null,
                                        'organisation_id'   => $organisation->id
                                    ]))->save();
                                }

                                ReportAwareness::where('id', '=', $p['id'])->update([
                                    'status' => 'sent'
                                ]);
                            }
                            else {
                                ReportAwareness::where('id', '=', $p['id'])->update([
                                    'deleted_at' => Carbon::now()
                                ]);
                            }

                            return true;
                        });
                    }
                }
                else {
                    ReportAwareness::where([
                        ['organisation_id', '=', $organisation->id],
                        ['status', '=', 'pending']
                    ])->update([
                        'deleted_at' => Carbon::now()
                    ]);

                }
            }
        });

        return 0;
    }
}