<?php

namespace App\Console\Commands\Report;

use App\Helpers\ClickhouseHandler;
use App\Interfaces\AkvoradoRepositoryInterface;
use App\Repositories\AkvoradoRepository;
use Carbon\Carbon;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Redis;

class ReportAttackEnrichThread extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'report-attack:enrich-thread {--pool=} {--thread=}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Correlates singular attacks against Akvorado data and builds an enriched object.';

    /**
     * Reserve an akvorado interface
     */

    private AkvoradoRepositoryInterface $akv;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->akv = new AkvoradoRepository();
        parent::__construct();
    }

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

        // Get list of relevant attack records
        $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
        );

        $pool       = $this->option('pool');
        $thread     = $this->option('thread');

        // Select the relevant singular attacks - We'll then need to segregate these out by attacks clearly over, and attacks potentially ongoing.
        $singular_attacks = collect($chh_reporting->selectUnpaged("SELECT
            report_singular.singular_id AS id,
            start_at::int as start_at,
            finish_at::int as finish_at,
            destination_addr
        FROM
            report_singular FINAL
        WHERE (start_at > now() - 86400) AND (finish_at > :from AND finish_at < :to) AND (report_singular.singular_id % $pool) = $thread AND report_singular.singular_id NOT IN (SELECT singular_id FROM report_singular_complete) ORDER BY singular_id ASC LIMIT 100", [
            'from' => Carbon::now()->subMinutes(360)->timestamp,
            'to' => Carbon::now()->subMinutes(15)->timestamp
        ])->get());

        $total_to_enrich = sizeof($singular_attacks);

        if($total_to_enrich == 0) {
            return 0;
        }

        echo "[$thread]: $total_to_enrich attacks will be processed.\n";

        $info_lookup = collect([]);

        foreach(array_chunk($singular_attacks->pluck('id')->toArray(), 2500) as $chunk) {
            $info_lookup = $info_lookup->merge(collect($chh_reporting->selectUnpaged("SELECT
                singular_id,
                start_at::int as start_at,
                finish_at::int as finish_at,
                destination_addr
                FROM
                    report_singular_info
                LEFT JOIN
                    report_singular FINAL ON report_singular.singular_id = report_singular_info.singular_id
                WHERE
                    singular_id IN (:singular)
            ", [ 'singular' => $chunk ])->get()));
        }

        $info_lookup = $info_lookup->keyBy('destination_addr');

        $now = Carbon::now()->timestamp;

        foreach($singular_attacks as $attack) {

            // Check whether attack is ongoing
            $ongoing = $attack['finish_at'] > ($now - 300);

            // Whether the attack is long
            $long = ($attack['finish_at'] - $attack['start_at'] > 3600);

            // Select an appropriate last window
            $last_window = $attack['start_at'];

            $specific_lookup = false;

            if(isset($info_lookup[$attack['destination_addr']])) {

                $specific_lookup = collect($info_lookup[$attack['destination_addr']]);

                $last_window = max($specific_lookup->pluck('finish_at')->toArray());

                if($last_window === null) {
                    $last_window = $specific_lookup['finish_at'];
                }
            }

            // Vars to pass into query
            $vars = [];

            // If it's ongoing, been more than an hour AND been more than an hour since last window...
            // Collect the last 50 minutes worth of data - Otherwise we can just wait
            if($ongoing && $long) {
                $hour_since = (Carbon::now()->timestamp - $last_window) > 3600;

                // Can collect if it's been an hour since...
                if($hour_since) {
                    $vars = [
                        $last_window == $attack['start_at'] ? ($last_window - 120) : $last_window, // Select two minutes before if first
                        $last_window + 3000,
                        $attack['destination_addr']
                    ];
                }
            }
            elseif(!$ongoing) { // Not ongoing
                $vars = [
                    $long ? $last_window : ($last_window - 120), // Select two minutes before if it'll be the first in the series
                    $attack['finish_at'] + 120,
                    $attack['destination_addr']
                ];
            }

            // One of two things will happen - Either we've seen it in the current state, or we haven't - We'll use redis as a quick hack here to avoid the specific lookup.
            $skippable =
                Redis::get('report-enrichment-ignore-' . $attack['id'] . '-' . $vars[0] . '-' . $vars[1]) !== null ||
                ($specific_lookup && $specific_lookup->where('start_at', '=', $vars[0])->where('finish_at', '=', $vars[1]));

            // We need to check whether the window is actually stored as well...
            if($skippable) {
                Redis::setex('report-enrichment-ignore-' . $attack['id'] . '-' . $vars[0] . '-' . $vars[1], 3600, '');
                $total_to_enrich--;    
            }
            else {
                try {

                    Http::timeout(600)->withBasicAuth('creatia', config('creatia.reporting.microservice_password'))->withHeaders(['Content-Type' => 'application/json'])->post(env('microservice_reporting_2_url') . '/creatia-report/enrich', [ 
                        'attacks' => [[ 'id' => (int)$attack['id'], 'address' => str_replace('::ffff:', '', $vars[2]), 'start' => $vars[0], 'end' => $vars[1], 'ongoing' => $ongoing ]]
                    ]);

                    Redis::setex('report-enrichment-ignore-' . $attack['id'] . '-' . $vars[0] . '-' . $vars[1], 3600, '');

                }
                catch(\Exception $ex) {
                    $this->log_error("Error enriching destination report.", [
                        'attack'        => $attack,
                        'exception'     => $ex
                    ]);
                }

                $total_to_enrich--;
            }

            echo "[$thread]: $total_to_enrich attacks left to process.\n";

        }

        if(sizeof($singular_attacks) !== 100) {
            echo "[$thread]: Sleeping for 10 seconds as there were not many attacks.\n";
            sleep(10);
        }

        return 0;
    }
}