<?php

namespace App\Modules\Network\Commands;

use Log;

use Illuminate\Support\Facades\DB;
use App\Models\Asns;
use App\Modules\Network\Models\Prefixes;
use App\Modules\Network\Models\Goliath\PrefixGoliathFlag;
use App\Modules\Network\Models\Goliath\GoliathAsnAttributes;
use App\Modules\Network\Models\Goliath\PrefixGoliathFirewall;
use App\Modules\Network\Models\Goliath\PrefixGoliathInline;
use App\Modules\Network\Models\PrefixCommunity;

class GoliathSetup extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'goliath:setup';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Sets up goliath objects for all prefixes.';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        try {
            Asns::select('id')->chunk(env('CHUNK_SIZE'), function($asn_chunk) {
                foreach($asn_chunk->pluck('id') as $asn) {
                    Prefixes::leftJoin('prefix_goliath_flags', 'prefix_goliath_flags.prefix_id', '=', 'prefixes.id')
                        ->leftJoin('prefix_goliath_inline', 'prefix_goliath_inline.prefix_id', '=', 'prefixes.id')
                        ->select(
                            'address',
                            'prefixes.id as relevant_id',
                            'prefixes.organisation_id',
                            DB::raw('prefix_goliath_flags.id as flag_id'),
                            DB::raw('prefix_goliath_inline.id as inline_id'),
                            'setup_required',
                            'prefixes.asn_id'
                        )
                        ->where('asn_id', '=', $asn)
                        ->where(function($y) {
                            $y->where(function($x) {
                                $x->whereNull('prefix_goliath_flags.prefix_id')->whereNull('prefix_goliath_flags.deleted_at');
                            });

                            $y->orWhere(function($x) {
                                $x->whereNull('prefix_goliath_inline.prefix_id')->whereNull('prefix_goliath_inline.deleted_at');
                            });

                            $y->orWhere(function($x) {
                                $x->where('setup_required', '=', 1);
                            });
                        })
                        ->where('setup_required', '!=', 2)
                        ->chunk(env('CHUNK_SIZE'), function($prefix_chunk) use($asn) {
                            $goliath_defaults = GoliathAsnAttributes::where('asn_id', '=', $asn)->get()->first() ?? collect([
                                'default_synproxy' => 0,
                                'default_chain' => null,
                                'default_inline' => 0,
                                'default_heuristics' => 3
                            ]);

                            $mercury_community_lookup = PrefixCommunity::where([['community', '=', '7578:90']])
                                ->whereIn('prefix_id', $prefix_chunk->pluck('relevant_id'))
                                ->get()
                                ->keyBy('prefix_id');

                            $insert_count = $update_count = 0;
                            
                            $to_insert = [];
                            $to_update = [];

                            $to_insert_inline = [];
                            $to_update_inline = [];

                            foreach($prefix_chunk as $prefix) {

                                if($prefix->flag_id == null) {
                                    ++$insert_count;
                                    $to_insert[] = isset($mercury_community_lookup[$prefix->relevant_id]) ? [
                                        'prefix_id' => $prefix->relevant_id,
                                        'flag_synproxy' => 1,
                                        'flag_heuristics' => 1,
                                        'flag_heuristics_level' => 3,
                                        'region_id' => null,
                                        'subregion_id' => null,
                                        'metro_id' => null
                                    ] : [
                                        'prefix_id' => $prefix->relevant_id,
                                        'flag_synproxy' => $goliath_defaults['default_synproxy'],
                                        'flag_heuristics' => $goliath_defaults['default_heuristics'] != 0 ? 1 : 0,
                                        'flag_heuristics_level' => $goliath_defaults['default_heuristics'],
                                        'region_id' => null,
                                        'subregion_id' => null,
                                        'metro_id' => null
                                    ];
                                }
                                else {
                                    ++$update_count;
                                    $to_update[] = $prefix->relevant_id;
                                }

                                if($prefix->inline_id == null || isset($mercury_community_lookup[$prefix->relevant_id])) {
                                    $to_insert_inline[] = [
                                        'prefix_id' => $prefix->relevant_id,
                                        'forced' => isset($mercury_community_lookup[$prefix->relevant_id]) ? 1 : $goliath_defaults['default_inline']
                                    ];
                                }
                                elseif($prefix->setup_required) {
                                    $to_update[] = $prefix->relevant_id;
                                    $to_update_inline[] = $prefix->relevant_id;
                                }
                            }

                            // Reset and clean up flags + inline...
                            PrefixGoliathFlag::whereIn('prefix_id', $to_update)->update([
                                'flag_synproxy' => $goliath_defaults['default_synproxy'],
                                'flag_heuristics' => $goliath_defaults['default_heuristics'] != 0 ? 1 : 0,
                                'flag_heuristics_level' => $goliath_defaults['default_heuristics'],
                                'region_id' => null,
                                'subregion_id' => null,
                                'metro_id' => null
                            ]);

                            PrefixGoliathInline::whereIn('prefix_id', $to_update_inline)->update([
                                'forced' => $goliath_defaults['default_inline'],
                            ]);
                            
                            // Delete chains since they're no longer relevant on a new setup...
                            PrefixGoliathFirewall::whereIn('prefix_id', $to_update)->delete();

                            // Insert new...
                            DB::table('prefix_goliath_flags')->insert($to_insert);
                            DB::table('prefix_goliath_inline')->insert($to_insert_inline);

                            \Log::debug("GOLIATH/SETUP - Insert: $insert_count, Update: $update_count");
                        });
                    
                }
            });
        }
        catch(\Exception $ex) {}

        return 0;
    }
}