<?php

namespace App\Console\Commands\Network;

use App\Console\Commands\BaseCommand;
use App\Helpers\Utility\UtilPrefix;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

use App\Models\Prefixes;

use App\Models\PrefixCommunity;

class PrefixCommunitiesV2 extends BaseCommand
{

    protected $signature = 'prefix:communities-v2';

    protected $description = 'Handles prefix-community association.';

    public function handle()
    {

        // Build up community dictionary
        $prefix_community_dictionary = [];
    
        foreach([...UtilPrefix::get_detailed(), ...UtilPrefix::get_detailed_v6()] as $announced_prefix) {
            $prefix_community_dictionary[$announced_prefix->prefix] = $announced_prefix->communities;
        }

        if(sizeof($prefix_community_dictionary) === 0) {
            return 0;
        }

        // Preamble

        $operation_time = Carbon::now();
       
        $known_prefixes = Prefixes::select('id', DB::raw('CONCAT(address, \'/\', mask) as prefix'))->where('still_announced', '=', 1)->get()->keyBy('prefix');
        $known_prefix_communities = PrefixCommunity::select('id', 'prefix_id', 'community')->whereIn('prefix_id', $known_prefixes->pluck('id'))->get()->groupBy('prefix_id');

        $to_insert = [];
        $to_delete = [];

        foreach($prefix_community_dictionary as $prefix => $current_communities) {

            if(isset($known_prefixes[$prefix])) {
                
                $prefix_id = $known_prefixes[$prefix]->id;
                $known_communities = isset($known_prefix_communities[$prefix_id]) ? $known_prefix_communities[$prefix_id] : collect([]);
                
                // To determine what to delete, we'll look at the known communities, check if they are in current, if not, remove.
                foreach($known_communities as $known_community) {
                    if(array_search($known_community->community, $current_communities) === false) {
                        $to_delete[] = $known_community->id;
                    }
                }

                // To determine inserts, look at current communities, check if they are known, if not, insert

                $plucked_communities = $known_communities->pluck('community')->toArray();

                foreach($current_communities as $current_community) {
                    if(array_search($current_community, $plucked_communities) === false) {
                        $to_insert[] = [
                            'prefix_id' => $prefix_id,
                            'is_large' => substr_count($current_community, ':') === 2 ? 1 : 0,
                            'community' => $current_community,
                            'created_at' => $operation_time,
                            'updated_at' => $operation_time,
                            'deleted_at' => null
                        ];
                    }
                }
            }
        }

        PrefixCommunity::onlyTrashed()->where('deleted_at', '<', Carbon::now()->subDays(30))->forceDelete();

        DB::beginTransaction();

        foreach(array_chunk($to_delete, 1000) as $chunk) {
            PrefixCommunity::whereIn('id', $chunk)->delete();
        }

        foreach(array_chunk($to_insert, 1000) as $chunk) {
            PrefixCommunity::insert($chunk);
        }

        DB::commit();

        \Log::info('Community change summary - Inserted: ' . sizeof($to_insert) . ', Deleted: ' . sizeof($to_delete));

        return 0;
    }
}
