<?php

namespace App\Modules\Network\Controllers;

// Laravel
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

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

// Creatia Default
use App\Helpers\Api\Netbox;
use App\Models\Asns;
use App\Models\Organisation;

// Mitigation
use App\Modules\Network\Models\Mercury\MercuryAppliance;
use App\Modules\Network\Models\Mercury\MercuryBgpSnapshot;
use App\Modules\Network\Models\Mercury\MercuryOrganisationAttributes;
use App\Modules\Network\Models\Mercury\MercuryTunnel;

use Log;

class AdminMercuryController extends MercuryController
{

    public function fetch_appliance_list(Request $_r) {
        
        // Build our query function
        $query_builder = function() {

            return MercuryAppliance::select(
                    'id',
                    'created_at as registered_at',
                    'name',
                    'tag',
                    'server_v4',
                    'server_v6',
                    'gateway',
                    'limit',
                    'hidden',
                    'locked',
                    'reported_at',
                    DB::raw('(SELECT count(*) FROM mercury_tunnels WHERE appliance_id = mercury_appliances.id AND deleted_at is null) as active_tunnels')
                );
        };

        // Build our search function
        $query_search = function($prefix_query, $global, $filters) {
            $columns = [
                'name', 'tag', 'server_v4', 'server_v6', 'gateway', 'limit'
            ];

            if($global) {
                $prefix_query = $prefix_query->where(function($query) use ($global, $columns) {
                    foreach($columns as $column) {
                        $query->orWhere($column, 'LIKE', "%$global%");
                    }
                });
            }

            if($filters) {
                foreach($filters as $filter) {
                    if( array_search($filter['id'], $columns) !== false ) {
                        $prefix_query = $prefix_query->where($filter['id'], 'LIKE', '%' . $filter['value'] . '%');
                    }
                }
            }

            return $prefix_query;
        };

        // Build our sorting function
        $query_sort = function($prefix_query, $column, $direction) {
            if($column != '') {
                return $prefix_query->orderBy($column, $direction);
            }

            return $prefix_query->orderByDesc('id');
        };

        // Build our transformation function
        $query_transform = function($data) {
            return $data;
        };

        return NetworkController::pagination_table_request($_r, $query_builder, $query_search, $query_sort, $query_transform, []);
    }

    public function fetch_appliance_options(Request $_r) {
        return response()->json([
            'appliances' => MercuryAppliance::select('id', 'name')->where(function($x) {
                $x->where('locked', '=', 0)->orWhereNull('locked');
            })->get()->toArray(),
            'asns' => Asns::select('id', 'asn')->where('organisation_id', '=', Auth::user()->organisation_id)->get()->toArray()
        ], 200);
    }

    public function fetch_tunnel_list(Request $_r) {                
        $draw = $_r->input('draw');
        $start = $_r->input('start');
        $length = $_r->input('length');
        $search_term = $_r->input('search_string');
        $order_direction = $_r->input('order_direction');
        $order_column = $_r->input('order_column');
        
        // Build our query function
        $query_builder = function() {

            $query = MercuryTunnel::select(
                'mercury_tunnels.id',
                'asns.asn as asn',
                'organisations.name as organisation',
                'mercury_appliances.name as appliance_name',
                'mercury_tunnels.name as tunnel_name',
                'bgp_state',
                'tunnel_state',
                'local_address',
                'remote_address',
                'remote_port',
                'gsl_device_v4',
                'gsl_device_v6',
                'client_device_v4',
                'client_device_v6',
                'type',
                'allow_announcements',
                'allow_downstreaming',
                'no_bgp'
                )
                ->leftJoin('mercury_appliances', 'appliance_id', 'mercury_appliances.id')
                ->leftJoin('asns', 'asn_id', 'asns.id')
                ->leftJoin('organisations', 'mercury_tunnels.organisation_id', '=', 'organisations.id');

            return $query;
        };

        // Build our search function
        $query_search = function($query, $global, $filters) {

            if($global) {
                // Look over every row, though ruleset implementation required...
                $query = $query->where(function($query) use($global) {
                    $query->orWhere('asn', 'LIKE', "%$global%")
                        ->orWhere('organisations.name', 'LIKE', "%$global%")
                        ->orWhere('mercury_appliances.name', 'LIKE', "%$global%")
                        ->orWhere('mercury_tunnels.name', 'LIKE', "%$global%")
                        ->orWhere('local_address', 'LIKE', "%$global%")
                        ->orWhere('remote_address', 'LIKE', "%$global%")
                        ->orWhere('remote_port', 'LIKE', "%$global%")
                        ->orWhere(DB::raw("CONCAT(remote_address, ':', remote_port)"), 'LIKE', "%$global%")
                        ->orWhere('gsl_device_v4', 'LIKE', "%$global%")
                        ->orWhere('gsl_device_v6', 'LIKE', "%$global%")
                        ->orWhere('client_device_v4', 'LIKE', "%$global%")
                        ->orWhere('client_device_v6', 'LIKE', "%$global%")
                        ->orWhere('type', 'LIKE', "%$global%");
                    });
            }

            if($filters) {
                foreach($filters as $filter) {
                    $col = '';
                    switch($filter['id']) {
                        case 'asn': $col = 'asn'; break;
                        case 'organisation': $col = 'organisations.name'; break;
                        case 'appliance_name': $col = 'mercury_appliances.name'; break;
                        case 'tunnel_name': $col = 'mercury_tunnels.name'; break;
                        case 'local_address': $col = 'local_address'; break;
                        case 'remote_port':
                        case 'remote_address': $col = DB::raw("CONCAT(remote_address, ':', remote_port)"); break;
                        case 'gsl_device_v4': $col = 'gsl_device_v4'; break;
                        case 'gsl_device_v6': $col = 'gsl_device_v6'; break;
                        case 'client_device_v4': $col = 'client_device_v4'; break;
                        case 'client_device_v6': $col = 'client_device_v6'; break;
                        case 'type': $col = 'type'; break;
                    }

                    if($col != '') {
                        $query = $query->where($col, 'LIKE', '%' . $filter['value'] . '%');
                    }
                }
            }

            return $query;
        };

        // Build our sorting function
        $query_sort = function($query, $column, $direction) {
            $col = 'mercury_tunnels.updated_at';

            switch($column) {
                case 'asn': $col = 'asn'; break;
                case 'organisation': $col = 'organisations.name'; break;
                case 'appliance_name': $col = 'mercury_appliances.name'; break;
                case 'tunnel_name': $col = 'mercury_tunnels.name'; break;
                case 'local_address': $col = 'local_address'; break;
                case 'remote_address': $col = DB::raw("CONCAT(remote_address, ':', remote_port)"); break;
                case 'gsl_device_v4': $col = 'gsl_device_v4'; break;
                case 'gsl_device_v6': $col = 'gsl_device_v6'; break;
                case 'client_device_v4': $col = 'client_device_v4'; break;
                case 'client_device_v6': $col = 'client_device_v6'; break;
                case 'type': $col = 'type'; break;
            }

            return $query->orderBy($col, !$direction ? 'desc' : $direction);

        };

        // Create valid sorting keys
        $sorting_keys = ['tunnel_name', 'organisation', 'asn', 'appliance_name', 'local_address', 'remote_port', 'remote_address', 'gsl_device_v4', 'gsl_device_v6', 'client_device_v4', 'client_device_v6', 'type'];

        // Build our transformation function
        $query_transform = function($data) {
            $data->transform(function($row) {
                return $row;
            });

            return $data;
        };

        return NetworkController::pagination_table_request($_r, $query_builder, $query_search, $query_sort, $query_transform, $sorting_keys);
    }

    public function get_tunnel(Request $_r, $_tunnel_id) {

        if($tunnel = MercuryTunnel::where([['id', '=', $_tunnel_id]])->get()->first()) {

            return response()->json([
                'tunnel_id' => $tunnel->id,
                'asn' => Asns::find($tunnel->asn_id)->asn,
                'appliance' => MercuryAppliance::find($tunnel->appliance_id)->name,
                'name' => $tunnel->name,
                'bgp_state' => $tunnel->bgp_state,
                'tunnel_state' => $tunnel->tunnel_state,
                'local_address' => $tunnel->local_address,
                'remote_address' => $tunnel->remote_port !== null ? "$tunnel->remote_address:$tunnel->remote_port" : $tunnel->remote_address,
                'gsl_device_v4' => $tunnel->gsl_device_v4,
                'gsl_device_v6' => $tunnel->gsl_device_v6,
                'client_device_v4' => $tunnel->client_device_v4,
                'client_device_v6' => $tunnel->client_device_v6,
                'type' => $tunnel->type,
                'allow_announcements' => $tunnel->allow_announcements,
                'allow_downstreaming' => $tunnel->allow_downstreaming,
            ], 200);

        }

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

    public function edit_tunnel(Request $_r, $_tunnel_id) {
        $valid_request = !Validator::make( array_merge([ 'tunnel_id' => $_tunnel_id ], $_r->input()),
            [
                'tunnel_id'         => [ 'required', Rule::in(MercuryTunnel::all()->pluck('id')) ],
                'name'              => [ 'string' ],
                'reprovision'       => [ 'boolean']
            ]
        )->fails();

        if(!$valid_request) {
            return response()->json([], 400);
        }

        $edit_tunnel = MercuryTunnel::find($_tunnel_id);

        if($_r->input('name', false)) {
            $edit_tunnel->name = $_r->input('name');
        }

        if($_r->input('reprovision', false)) {
            $attributes = MercuryOrganisationAttributes::where('organisation_id', '=', Auth::user()->organisation_id)->get()->first();

            $edit_tunnel->bgp_state = 'UPDATE';
            $edit_tunnel->tunnel_state = 'UPDATE';
            $edit_tunnel->allow_announcements = $attributes['allow_announcements'];
            $edit_tunnel->allow_downstreaming = $attributes['allow_downstreaming'];
        }

        $edit_tunnel->save();

        return response()->json([
            'tunnel_id' => $edit_tunnel->id
        ], 200);
    }

    public function delete_tunnel(Request $_r, $_tunnel_id) {
        if($delete_tunnel = MercuryTunnel::where([['id', '=', $_tunnel_id]])->get()->first()) {
            
            $delete_tunnel->update([
                'bgp_state'     => 'DELETE',
                'tunnel_state'  => 'DELETE'
            ]);

            $delete_tunnel->delete();

            return response()->json('SUCCESS', 200);
        }

        return response()->json('FAILURE', 400);
    }

    public function get_attributes(Request $_r, $_organisation_id) {
        $defaults = MercuryOrganisationAttributes::where('organisation_id', '=', $_organisation_id)->get()->first();
        
        return response()->json($defaults === null ? [
            'vip'                   => false,
            'allow_high'            => false,
            'allow_announcements'   => true,
            'allow_downstreaming'   => false,
            'allow_unknowns'        => false,
            'aggregate_bandwidth'   => null
        ] : [
            'vip'                   => $defaults->vip,
            'allow_high'            => $defaults->allow_high,
            'allow_announcements'   => $defaults->allow_announcements,
            'allow_downstreaming'   => $defaults->allow_downstreaming,
            'allow_unknowns'        => $defaults->allow_unknowns,
            'aggregate_bandwidth'   => $defaults->aggregate_bandwidth
        ], 200);
    }

    public function put_attributes(Request $_r, $_organisation_id) {

        // Validate this request in the first place.
        $valid_request = !Validator::make( array_merge(['organisation_id' => $_organisation_id], $_r->all()), [
            'organisation_id'       => ['required', Rule::in(Organisation::all()->pluck('id'))],
            'vip'                   => ['nullable', 'boolean'],
            'allow_high'            => ['nullable', 'boolean'],
            'allow_announcements'   => ['nullable', 'boolean'],
            'allow_unknown'         => ['nullable', 'boolean'],
            'allow_downstreaming'   => ['nullable', 'boolean'],
            'aggregate_bandwidth'   => ['nullable', 'integer']

        ])->fails();

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

        $defaults =  MercuryOrganisationAttributes::firstOrNew(['organisation_id' => $_organisation_id]);

        $defaults->vip                  = $_r->input('vip', false);
        $defaults->allow_high           = $_r->input('allow_high', false);
        $defaults->allow_announcements  = $_r->input('allow_announcements', false);
        $defaults->allow_downstreaming  = $_r->input('allow_downstreaming', false);
        $defaults->allow_unknowns       = $_r->input('allow_unknown', false);
        $defaults->aggregate_bandwidth  = $_r->input('aggregate_bandwidth', false) ? $_r->input('aggregate_bandwidth') : null;

        if($defaults->save()) {
            return response()->json(['detail' => __('api.200_GENERIC')], 200);
        }

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

}
