<?php

namespace App\Console\Commands\Network;

use App\Console\Commands\BaseCommand;
use App\Models\GeoIp\PrefixGeoIp;
use App\Models\Goliath\GoliathAttackPrefix;
use App\Models\Goliath\PrefixGoliathFirewall;
use App\Models\Goliath\PrefixGoliathFlag;
use App\Models\Goliath\PrefixGoliathInline;
use App\Models\Goliath\PrefixGoliathLoose;
use App\Models\PrefixAsnPeers;
use App\Models\PrefixCommunity;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

use App\Models\Prefixes;
use App\Models\PrefixExclusions;
use App\Models\PrefixSubtenants;
use App\Models\Supernets;
use Illuminate\Support\Facades\Log;

class PrefixDelete extends BaseCommand
{
    
    protected $signature = 'prefix:delete';

    protected $description = 'Deletes prefixes that have not been recently seen, and their assocations.';

    public function handle()
    {
        /**
         * Perform our soft-deletion so we can phase things out of queries and visibility
         */

        // Work out addresses that should be excluded from the cleanup process
        $exclusion_cidrs = PrefixExclusions::select(DB::raw("CONCAT(address,'/',mask) as cidr"))->get()->pluck('cidr');
        $exclusion_ids = Prefixes::whereIn(DB::raw("CONCAT(address,'/',mask)"), $exclusion_cidrs)->get()->pluck('id');

        // Work out what could be deleted - Only select largest supernets, ergo those with no supernets above them.
        $removable_ids = Prefixes::leftJoin('supernets', 'subnet_id', '=', 'prefixes.id')
            ->select(DB::raw('count(*)'),'prefixes.id')
            ->whereNotIn('prefixes.id', $exclusion_ids)
            ->where('seen_at', '<', Carbon::now()->subDays(14))->groupBy('prefixes.id')
            ->having(DB::raw('count(*)'), '=', '1')
            ->pluck('id');

        DB::beginTransaction();

        foreach($removable_ids as $affected) {
            if(($affected_found = Prefixes::find($affected)) && $affected_found !== null) {
                $affected_found->delete();
            }
        }

        DB::commit();

        /**
         * Perform our hard-deletion of anything that has been deleted for ninety days or more - It won't be needed at this point.
         */

        $purgable_ids = Prefixes::onlyTrashed()->where('deleted_at', '<', Carbon::now()->subDays(90))->pluck('id');

        if(sizeof($purgable_ids) !== 0) {

            $chunks = array_chunk($purgable_ids->toArray(), 5000);

            Log::info('Purging ' . sizeof($purgable_ids) . ' prefixes as they have been deleted for a while.');

            foreach($chunks as $i => $x) {

                Log::info("Purging chunk $i / " . sizeof($chunks) - 1);

                try {
                    DB::beginTransaction();

                    PrefixGoliathFlag::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    PrefixGoliathInline::withTrashed()->whereIn('prefix_id', $x)->forceDelete();                
                    PrefixGoliathFirewall::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    PrefixGoliathLoose::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    PrefixCommunity::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    PrefixAsnPeers::whereIn('prefix_id', $x)->forceDelete();
                    PrefixSubtenants::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    PrefixGeoIp::withTrashed()->whereIn('prefix_id', $x)->forceDelete();
                    Supernets::whereIn('subnet_id', $x)->forceDelete();
                    Supernets::whereIn('supernet_id', $x)->forceDelete();
                    GoliathAttackPrefix::whereIn('prefix_id', $x)->forceDelete();
                    Prefixes::onlyTrashed()->whereIn('id', $x)->forceDelete();

                    DB::commit();

                    Log::info("Successfully purged chunk $i / " . sizeof($chunks) - 1);
                }
                catch(\Throwable $t) {
                    Log::error("Failed to purge chunk $i / " . (sizeof($chunks) - 1) . ' - Rolling back chunk.');
                    DB::rollBack();
                }

            }

        }


        /**
         * Cleanup prefix setup cruft
         */
        Prefixes::where('setup_required', '=', 1)->update([
            'setup_required' => 0
        ]);

        return 0;
    }
}