<?php

namespace App\Http\Controllers\Provisioning;

// Laravel
use App\Models\Asns;
use App\Models\AsSets;
use App\Models\Organisation;
use App\Models\OrganisationExternalIdentities;
use App\Rules\AdminAnyAsnIdentity;
use App\Rules\AdminAnyAsSetIdentity;
use App\Rules\Fragments\PagedTable;
use App\Rules\OrganisationIdentity;
use App\Rules\SidHash;
use Illuminate\Http\Request;

use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Validator;

class AdminBgpController extends BgpController
{

    public function __construct()
    {

        if(config('app.demonstration')) {
            Http::fake([
                'http://localhost:56000/scan/cxid/*' => Http::response((function() {

                    $data = json_decode(file_get_contents(app_path('Demonstration/BgpPortsDemo.json')), true);

                    return json_encode($data['list']);

                })(), 200),
                
                'http://localhost:56000/live/details/Auckland-Router' => Http::response((function() {
                    return json_encode(json_decode(file_get_contents(app_path('Demonstration/BgpPortsDemo.json')), true)['ports']['Auckland-Router']);
                })(), 200),

                'http://localhost:56000/live/details/Ashburn-Router' => Http::response((function() {
                    return json_encode(json_decode(file_get_contents(app_path('Demonstration/BgpPortsDemo.json')), true)['ports']['Ashburn-Router']);
                })(), 200),

                'http://localhost:56000/live/details/Singapore-Router' => Http::response((function() {
                    return json_encode(json_decode(file_get_contents(app_path('Demonstration/BgpPortsDemo.json')), true)['ports']['Singapore-Router']);
                })(), 200),
                
                'http://localhost:56000/peeringdb/asn/137409*' => Http::response(
                    json_decode('{"network_name":"Testing w/ 137409","irr_as_set":null,"info_prefixes4":50,"info_prefixes6":100,"asn":137409,"ipv4_subnet":"10.16.0.2/31","ipv6_subnet":"2401:3cc0::4:502/127","ipv4_is_private":true,"suggested_ipv4_hash":"8fefb3e0aa49e98b163ca9c80beb318a97a48ff2ae76e58bf3423cc5deb566c0","suggested_ipv6_hash":"9abcd296f4fb4c8d5163a85a08c6319af83fd4bec95472c7a04db54136245b24"}', true),
                    200
                ),

                'http://localhost:56000/provision/add-bgp-session*' => Http::response(
                    json_decode('{"message": "Live mode: BGP session request accepted for SID"}', true),
                    200
                ),

                '*' => Http::response([ 'Fallback Error' ], 500) // Fallback
            ]);

        }
        
        parent::__construct();
    }
 
    public function get_full_sid_list(Request $_r, $_type, $_identifier) {

        // Validate input information.
        $validated = Validator::make([
            ...$_r->all(),
            'type' => $_type,
            'identifier' => $_identifier
        ], [
            ...PagedTable::rules(['community', 'description', 'type']),
            'type' => Rule::in(['organisation', 'billing']),
            'identifier' => $_type === 'organisation' ? new OrganisationIdentity : 'integer'
        ]);

        if($validated->fails()) {
            return response()->json(['detail' => 'Invalid inputs supplied.'], 400);
        }

        // Get the users WHMCS ID
        $ids = $_type === 'organisation' ? 
            (OrganisationExternalIdentities::where([['key', '=', 'gsl'], ['organisation_id', '=', $_identifier]])->get()->pluck('identity')->toArray() ?? [])
            : [ $_identifier ];
            
        if($ids === []) {
            return response()->json([
                'draw' => (int)$_r->input('draw', 0),
                'recordsTotal' => 0,
                'recordsFiltered' => 0,
                'data' => []
            ], 200);
        }

        // Use it to make calls to our provisioning system
        $raw_rows = [];

        foreach($ids as $id) {
            $raw_rows = [...$raw_rows, ...$this->helper_scan_cxid($id)];
        }

        // Iterate over each batch interface to build up the true logical representation of the super-interfaces
        $processed_rows = [];

        foreach($raw_rows as $i => $superinterface) {

            if($superinterface['batch_interfaces'] !== null && sizeof($superinterface['batch_interfaces']) !== 0) {

                foreach($superinterface['batch_interfaces'] as $j => $subinterface) {

                    // We only care IF it is the parent interface
                    if($subinterface['parent_interface_name'] === null) {

                        // Cache the temporary identifiers against a distinct identifiable. We'll just use a session.
                        $redis_hash = $this->get_sid_hash("$i-$j", $superinterface['display_hint']);
                        $redis_string = $this->get_sid_key($redis_hash);

                        Redis::setex($redis_string, 600, $superinterface['temp_id']);

                        $processed_rows[] = [
                            'id'                    => $redis_hash,
                            'interface_type'        => $subinterface['interface_type'],
                            'interface_name'        => $subinterface['interface_name'],
                            'router_name'           => $superinterface['display_hint'],
                            'is_unsupported'        => $superinterface['is_unsupported']
                        ];

                    }
                }
            }
            else {
                if($superinterface['parent_interface_name'] === null) {
                    // Cache the temporary identifiers against a distinct identifiable. We'll just use a session.
                    $redis_hash = $this->get_sid_hash("$i", $superinterface['display_hint']);
                    $redis_string = $this->get_sid_key($redis_hash);

                    Redis::setex($redis_string, 600, $superinterface['temp_id']);

                    $processed_rows[] = [
                        'id'                    => $redis_hash,
                        'interface_type'        => $superinterface['interface_type'],
                        'interface_name'        => '-',
                        'router_name'           => $superinterface['display_hint'],
                        'is_unsupported'        => $superinterface['is_unsupported'],
                    ];
                }
            }
        }

        return response()->json([
            'draw' => (int)$_r->input('draw', 0),
            'recordsTotal' => sizeof($processed_rows),
            'recordsFiltered' => sizeof($processed_rows),
            'data' => $processed_rows
        ], 200);
    }

    public function get_sid_details(Request $_r, $_id) {

        // Validate this request in the first place.
        $valid_request = !Validator::make( [ 'uuid' => $_id ], [
            'uuid'    => ['required', 'string', new SidHash]
        ])->fails();

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

        // Take a distinct identifiable, check the session for this information, and then use the pulled out UUID to get the SID details.
        if(($uuid = Redis::get($this->get_sid_key($_id))) && $uuid !== null) {

            // Use it to make calls to our provisioning system
            $raw_rows = $this->helper_get_details($uuid);

            $processed_parentals = [];
            $processed_children = [];
            $processed_sessions = [];

            foreach($raw_rows as $row) {

                unset($row['temp_id']);

                if($row['parent_interface_name'] === null) {
                    $processed_parentals[$row['interface_name']] = $row;

                    if(isset($row['details']) && isset($row['details']['bgp_details']) && $row['details']['bgp_details'] !== []) {
                        $processed_sessions[$row['interface_name']] = isset($processed_sessions[$row['interface_name']]) ? [
                            ...$processed_sessions[$row['interface_name']],
                            ...$row['details']['bgp_details']
                        ] : [
                            ...$row['details']['bgp_details']
                        ];
                    }
                }
                else {
                    $processed_children[$row['parent_interface_name']][$row['interface_name']] = $row;

                    if(isset($row['details']) && isset($row['details']['bgp_details']) && $row['details']['bgp_details'] !== []) {
                        $processed_sessions[$row['parent_interface_name']] = isset($processed_sessions[$row['parent_interface_name']]) ? [
                            ...$processed_sessions[$row['parent_interface_name']],
                            ...$row['details']['bgp_details']
                        ] : [
                            ...$row['details']['bgp_details']
                        ];
                    }
                }
            }
            
            return response()->json([
                'parent'    => $processed_parentals,
                'children'  => $processed_children,
                'bgp'       => $processed_sessions
            ], 200);
        }

        return response()->json([], 400);
    }

    public function get_asn_details(Request $_r) {

        // Validate this request in the first place.
        $valid_request = !Validator::make( $_r->all(), [
            'asn_id'    => ['required', AdminAnyAsnIdentity::make(true)],
            'sid'       => ['required', 'string'],
            'identity'  => ['required', 'string', new SidHash],
        ])->fails();

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

        // Take a distinct identifiable, check the session for this information, and then use the pulled out UUID to get the SID details.
        if(($uuid = Redis::get($this->get_sid_key($_r->input('identity')))) && $uuid !== null) {

            // Use it to make calls to our provisioning system
            $information = $this->helper_get_asn(Asns::find($_r->input('asn_id'))->asn, $_r->input('sid'), $uuid );
            
            return response()->json([
                'info' => $information,
            ], 200);
        }

        return response()->json([], 400);
    }
    
    public function post_bgp_session(Request $_r) {

        // Validate this request in the first place.
        $valid_request = !Validator::make( $_r->all(), [
            'identity'          => ['required', 'string', new SidHash],
            'sid'               => ['required', 'string'],
            'v4_hash'           => ['required', 'string'],
            'v6_hash'           => ['required', 'string'],
            'asn_id'            => ['required', AdminAnyAsnIdentity::make(true)],
            'as_set_id'         => ['nullable', AdminAnyAsSetIdentity::make(true)],
            'filter'            => ['required', Rule::in(['full', 'full-nodefault', 'default', 'denyall'])],
            'vlan'              => ['nullable', 'integer', 'min:2', 'max:4094'],
            'v4_max'            => ['nullable', 'integer', 'min:0'],
            'v6_max'            => ['nullable', 'integer', 'min:0']
        ])->fails();

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

        // Take a distinct identifiable, check the session for this information, and then use the pulled out UUID to get the SID details.
        if(($uuid = Redis::get($this->get_sid_key($_r->input('identity')))) && $uuid !== null) {

            $asn = Asns::find($_r->input('asn_id'))->asn;

            $v4_max = $_r->input('v4_max', null) ?? 1;
            $v6_max = $_r->input('v6_max', null) ?? 1;

            \Log::info("Session call of: " . json_encode([$uuid, $_r->input('sid'), $_r->input('v4_hash'), $_r->input('v6_hash'), $asn, null, $_r->input('filter'), $_r->input('vlan'), ($v4_max == 0 ? 1 : $v4_max), ($v6_max == 0 ? 1 : $v6_max)]));
            $session_add = $this->helper_add_session($uuid, $_r->input('sid'), $_r->input('v4_hash'), $_r->input('v6_hash'), $asn, null, $_r->input('filter'), $_r->input('vlan'), ($v4_max == 0 ? 1 : $v4_max), ($v6_max == 0 ? 1 : $v6_max));

            if(str_contains($session_add['message'], 'BGP session request accepted')) {
                return response()->json([], 200);                
            }

            return response()->json([], 500);
        }
        return response()->json([], 400);
    }

    public function get_options(Request $_r) {

        $billing_information = Http::get(env('ENDPOINT_BILLING_MICROSERVICE_URL'), [ 'key' => env('ENDPOINT_BILLING_MICROSERVICE_SECRET') ]);

        // Accounts
        $billing_accounts = [];

        foreach($billing_information['customer_ids'] as $item) {

            if(isset($item['id'])) {

                if($item['id'] > 1000000) {
                    continue;
                }

                $billing_accounts[] = [
                    'id'        => $item['id'],
                    'name'      => $item['name'] . ' (' . $item['id'] . ')',
                ];
            }
        }

        return response()->json([
            'billing' => $billing_accounts,
            'organisation' => Organisation::select('id', 'name')->whereIn('id', OrganisationExternalIdentities::where('key', '=', 'gsl')->select('organisation_id')->get()->pluck('organisation_id'))->get()->toArray()
        ]);

    }

}