<?php

namespace App\Http\Controllers\Goliath;

use App\Models\Goliath\GoliathAttackPrefix;
use App\Models\WebhookMessage;
use App\Repositories\ReportRepository;
use App\Rules\Fragments\PagedTable;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\StreamedResponse;

class AdminReportController extends ReportController
{
    protected $admin = true;

    public function __construct()
    {
        parent::__construct();
    }

    public function fetch_notifications(Request $_r) {
        return $this->paged_table($_r, function() {
            return WebhookMessage::leftJoin('webhooks', 'webhook_id', '=', 'webhooks.id')
                ->join('organisations', 'organisations.id', '=', 'webhook_messages.organisation_id')
                ->where('message_type', '=', 'reporting')
                ->select(
                    'webhook_messages.created_at as queued_at',
                    'sent_at',
                    'webhook_messages.id as message_id',
                    'status',
                    'payload as message',
                    'hook_type',
                    'webhooks.name as hook_name',
                    'webhook_messages.organisation_id as organisation_id',
                    'organisations.name as organisation_name'
                );
        }, [ 
            'queued_at'     => [ 'db' => 'webhook_messages.created_at', 'searchable' => true ],
            'sent_at'       => [ 'db' => 'sent_at', 'searchable' => true ],
            'message_id'       => [ 'db' => 'webhook_messages.id', 'searchable' => true ],
            'message'       => [ 'db' => DB::raw('payload::text'), 'searchable' => true ],
            'hook_type'       => [ 'db' => DB::raw('hook_type::text'), 'searchable' => true ],
            'hook_name'       => [ 'db' => 'webhooks.name', 'searchable' => true ],
            'organisation_name'  => [ 'db' => 'organisations.name', 'searchable' => true ],
        ], 'webhook_messages.created_at', null);
    }

    public function fetch_leaderboard(Request $_r) {

        $valid_request = !Validator::make($_r->all(), [
            ...PagedTable::rules([ 'organisation', 'max_peak_gbps', 'avg_peak_gbps', 'median_peak_gbps', 'sum_gbps', 'mean_duration', 'median_duration', 'events', 'c_score' ]),
            'attack_start' => ['required', 'date'],
            'attack_finish' => ['required', 'date'],
            'attack_size' => ['required', 'integer'],
        ])->fails();

        if(!$valid_request) {
            return response()->json(['detail' => __('api.422_INVALID_PAYLOAD')], 422);
        }

        return response()->json((new ReportRepository(true))->get_table_leaderboard(
            $_r->input('draw'),
            $_r->input('start'),
            $_r->input('length'),
            $_r->input('order_column'),
            $_r->input('order_direction'),
            $_r->input('attack_start'),
            $_r->input('attack_finish'),
            $_r->input('attack_size')
        ), 200);

    }

    public function fetch_single(Request $_r) {

        $valid_request = !Validator::make($_r->all(), [
            ...PagedTable::rules(['destination', 'finish_at', 'start_at', 'gbps', 'mpps']),
            'filter.global' => ['nullable', 'regex:/([\d\.\-\:A-Za-z])/'],
            'filter.column.destination' => ['nullable', 'regex:/([\d\.])/'],
            'filter.column.finish_at' => ['nullable', 'regex:/([\d\-\:])/'],
            'filter.column.start_at' => ['nullable', 'regex:/([\d\-\:])/'],
            'filter.column.organisation' => ['nullable', 'string']
        ])->fails();

        if(!$valid_request) {
            return response()->json(['detail' => __('api.422_INVALID_PAYLOAD')], 422);
        }

        $this->log_event('AdminReportController::fetch_single', $_r->input());

        try {
            $draw = $_r->input('draw');
            $start = $_r->input('start', 0);
            $length = $_r->input('length', 100);
            $raw_filters = $_r->input('filter', []);
            $order_direction = $_r->input('order_direction', 'DESC');
            $order_column = $_r->input('order_column', 'start_at');

            $filters = [];

            if(isset($raw_filters['global'])) {
                $filters['global'] = $raw_filters['global'];
            }

            if(isset($raw_filters['column'])) {
                $filters = array_merge($filters, $raw_filters['column']);
            }

            $cached = Redis::get(strtolower("report-singular-admin-list-$order_column-$order_direction"));

            if(array_search($order_column, ['start_at', 'gbps', 'mpps']) !== false && $cached !== null && $start == 0 && $length == 100 && sizeof($filters) == 0) {
                return response()->json([ ...json_decode($cached, true), 'draw' => (int)$draw ], 200);
            }

            $reports = $this->rr->get_list_singular($draw, $filters, $start, $length, $order_column, $order_direction);

            return response()->json($reports, 200); 
        } catch(\Throwable $ex) {
            $this->log_error('AdminReportController::fetch_single', $_r->input(), $ex);
        }

        return response()->json([
            'draw' => 0,
            'recordsTotal' => 0,
            'recordsFiltered' => 0,
            'data' => []
        ], 200);
    }

    public function report_single(Request $_r, $_report_id) {
    
        // Run validator
        $validator = Validator::make( [ 'report_id' => $_report_id ], [ 'report_id' => 'integer' ]);

        if($validator->fails()) {
            return response()->json(['detail' => 'Unauthorised'], 400);
        }

        $this->log_event('AdminReportController::report_single', $_r->input());

        // Get the organisation ID for the report since we gate the cached reports behind organisation ID
        $report_organisation = GoliathAttackPrefix::join('prefixes', 'prefixes.id', '=', 'prefix_id')->where('attack_id', '=', $_report_id)->select('organisation_id')->first();
        $report_organisation_id = $report_organisation === null ? 0 : $report_organisation->organisation_id;
        
        // Check if we have a cached value - If we don't, retrieve it the old fashioned way.
        $cached = Redis::get("report-$report_organisation_id-$_report_id");

        $report = null;

        if($cached !== null) {
            $report = base64_decode(gzinflate($cached));
        }
        else {
            $report = $this->rr->get_report_singular($_report_id, null);
        }

        $this->log_event('AdminReportController::report_single', $_r->input(), $cached === null ? 'Fresh report downloaded' : 'Cached report downloaded');

        if(!$report) {
            return response()->json(['detail' => 'Error processing the request'], 500);
        }

        // Stream report back
        try {
            return new StreamedResponse(function () use ($report) { echo $report; }, 200, [
                'Content-Type' => 'application/pdf',
                'Content-Disposition' => "attachment; filename=\"DDoS Attack Report $_report_id.pdf\"",
            ]);
        }
        catch(\Throwable $ex) {
            $this->log_error('AdminReportController::report_single', $_r->input(), $ex);
        }

        return response()->json(['detail' => 'Error processing the request'], 500);
    }
}