<?php

namespace App\Console\Commands\Report;

use App\Interfaces\ReportRepositoryInterface;
use App\Models\Organisation;
use App\Repositories\ReportRepository;
use Illuminate\Support\Facades\Redis;

class ReportCacheDestinationTable extends \App\Console\Commands\BaseCommand
{

    protected $signature = 'report:cache-destination-table';

    protected $description = 'This serves to cache each organisations list of destination reports.';

    protected ReportRepositoryInterface $rr;

    public function __construct()
    {
        $this->rr = new ReportRepository();

        parent::__construct();
    }

    private function get_list($_organisation_id = null)
    {
        return $this->rr->get_list_singular(1, $_organisation_id === null ? [] : [ 'organisation_id' => $_organisation_id ], 0, 100, 'start_at', 'DESC');
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        if(env('reporting_disabled')) {
            $this->log_warning("Not running as `reporting_disabled` is set in the .env file.", []);
            return 0;
        }

        // Track start
        $start = microtime(true);

        // We can quickly check whether things have changed at all using our automation list, which retrieves much faster and is used to quickly determine if 
        $stored_automation_list = Redis::get('report-singular-automation-list');
        $current_automation_list = $this->rr->get_list_automation_singular(1, [], 0, 100000, 'start_at', 'DESC');

        if(hash('sha512', $stored_automation_list) === hash('sha512', json_encode($current_automation_list, JSON_UNESCAPED_SLASHES))) {
            return 0;
        }

        Redis::setex('report-singular-automation-list', 172800, json_encode($current_automation_list));

        // Get things we'll be reusing
        $organisations = Organisation::select('id', 'name')->get();

        // Retrieve the admin list then compare it to the stored list. If they're different, onward we go. Otherwise bail out.
        $stored_admin_list = Redis::get('report-singular-admin-list');
        $current_admin_list = $this->get_list(null);

        if(hash('sha512', $stored_admin_list) === hash('sha512', json_encode($current_admin_list, JSON_UNESCAPED_SLASHES))) {
            return 0;
        }

        // Reserve the base path as it'll be reused.
        $base = base_path();

        $organisations_to_cache = [];

        if(!$stored_admin_list || $stored_admin_list === []) {
            $organisations_to_cache = $organisations->pluck('id');
        }
        else {
            // Get stored counts
            $stored_counts = []; 
            
            foreach(json_decode($stored_admin_list, true)['data'] as $i) {
                if(isset($stored_counts[$i['organisation_id']])) {
                    ++$stored_counts[$i['organisation_id']];
                }
                else {
                    $stored_counts[$i['organisation_id']] = 1;
                }
            }

            // Get new counts
            $new_counts = []; 

            foreach($current_admin_list['data'] as $i) {
                if(isset($new_counts[$i['organisation_id']])) {
                    ++$new_counts[$i['organisation_id']];
                }
                else {
                    $new_counts[$i['organisation_id']] = 1;
                }
            }

            foreach($new_counts as $org_id => $count) {

                if(!isset($stored_counts[$org_id])) {
                    $organisations_to_cache[] = $org_id;
                }
                elseif($count !== $stored_counts[$org_id]) {
                    $organisations_to_cache[] = $org_id;
                }
            }
        }

        // Cache tables for organisations
        $organisations_to_cache = array_unique(is_array($organisations_to_cache) ? $organisations_to_cache : $organisations_to_cache->toArray());

        foreach(array_chunk($organisations_to_cache, 4) as $i => $chunk) {

            $pipes = [];

            foreach($chunk as $j) {
                $pipes[] = popen("php $base/artisan report:cache-destination-table-thread --organisation=" . (int)$j . " --order=start_at --direction=desc", 'w');
            }

            foreach($pipes as $id => $pipe) {
                $exit = pclose($pipes[$id]);
                if($exit !== 0) {
                    $this->log_warning("When caching the individual organisation report tables, we had a non-zero exit code.", [
                        'id' => $chunk[$id],
                        'exit-code' => $exit
                    ]);
                }
            }
        }

        // Cache variants of the admin table
        foreach(array_chunk([
            [ 'start_at', 'desc' ],
            [ 'start_at', 'asc' ],
            [ 'gbps', 'desc' ],
            [ 'gbps', 'asc' ],
            [ 'mpps', 'desc' ],
            [ 'mpps', 'asc' ]
        ], 3) as $i => $chunk) {

            $pipes = [];

            foreach($chunk as $j) {
                $key = $j[0];
                $dir = $j[1];

                $pipes[] = popen("php $base/artisan report:cache-destination-table-thread --organisation=0 --order=$key --direction=$dir", 'w');
            }

            foreach($pipes as $id => $pipe) {
                $exit = pclose($pipes[$id]);

                if($exit !== 0) {
                    $this->log_warning("When caching the admin organisation report tables, we had a non-zero exit code.", [
                        'chunk' => $chunk[$id],
                        'exit-code' => $exit
                    ]);
                }
            }
        }

        // Finally, cache the admin list. 
        Redis::setex('report-singular-admin-list', 172800, json_encode($current_admin_list, JSON_UNESCAPED_SLASHES));

        if(($time_taken = microtime(true) - $start) && $time_taken > 30) {
            $this->log_warning("Execution time exceeded 30 seconds: $time_taken\s", []);
        }

        return 0;
    }
}