<?php

namespace App\Console\Commands\Report;

use App\Helpers\Utility\SymEncrypt;
use App\Models\Goliath\ReportLinks;
use App\Models\Organisation;
use App\Models\PendingReport;
use App\Models\PendingReportStatus;
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;
use Illuminate\Support\Str;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Queue up report links 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_individual',
            'send_webhook'
        )->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 = PendingReport::where([['organisation_id', '=', $organisation->id], ['status', '=', PendingReportStatus::Pending->value]])->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['destination'], 'time' => Carbon::now() ] ])[0] === false) {

                            $this->log_error('Bad queue attempt for destination report.', [
                                'organisation' => $organisation->id,
                                'report' => $p
                            ]);
                            
                            PendingReport::where('id', '=', $p['id'])->update([
                                'status' => PendingReportStatus::Denied
                            ]);
                            
                            continue;
                        }

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

                            if($organisation->send_individual != 0) {
                                // Send it along each valid webhook
                                foreach($hooks as $hook) {

                                    // Produce the link for the pending report
                                    $token = '';

                                    do {
                                        $token = Str::random(128);
                                    }
                                    while(ReportLinks::where('token', '=', $token)->count());

                                    $link_obj = new ReportLinks([
                                        'token'     => $token,
                                        'type'      => 'disposable',
                                        'address'   => null,
                                        'report_id' => $p['id']
                                    ]);

                                    $link_obj->save();

                                    $dst = $p['destination'];
                                    $start = Carbon::parse($p['started_at'])->format('jS F Y H:i:s');
                                    $finish = Carbon::parse($p['finished_at'])->format('jS F Y H:i:s');

                                    // Build up notification for shunting
                                    $notification = [
                                        'The report for a single destination attack is ready. Information is as follows:',
                                        "Destination: $dst/32",
                                        "Start: $start",
                                        "Finish: $finish",
                                        'Report URL: ' . url("api/guest/report/download/$token")
                                    ];

                                    (new WebhookMessage([
                                        'key'               => 'rs-' . $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();
                                }

                                PendingReport::where('id', '=', $p['id'])->update([
                                    'status' => PendingReportStatus::Sent
                                ]);
                            }
                            else {
                                PendingReport::where('id', '=', $p['id'])->update([
                                    'status' => PendingReportStatus::Denied
                                ]);
                            }

                            return true;
                        });
                    }
                }
                else {
                    PendingReport::where([
                        ['organisation_id', '=', $organisation->id],
                        ['status', '=', PendingReportStatus::Pending]
                    ])->update([
                        'status' => PendingReportStatus::NotForSend
                    ]);

                }
            }
        });

        return 0;
    }
}