<?php

namespace App\Modules\Network\Controllers;

// Laravel
use Carbon\Carbon;
use Illuminate\Http\Request;

use Illuminate\Support\Facades\Validator;

use Illuminate\Support\Facades\Notification;

// Creatia Default
use App\Models\StateTracker;
use App\Modules\Network\Controllers\NetworkController;
use App\Notifications\SlackNotification;

// Mercury
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 AgentMercuryController extends NetworkController
{

    public function fetch_update(Request $_r, $_tag = null) {
        if($_tag === null) {
            // Do nothing
        }

        try {

            // Grab tunnels, as well as organisation attributes where possible...
            $organisation_attributes = MercuryOrganisationAttributes::all()->keyBy('organisation_id');

            // Grab speed limits...
            $speed_limits = StateTracker::distinct('identifier')->where([['identifier', 'LIKE', 'MER-L:%'], ['created_at', '>', Carbon::now()->subHours(1)]])->orderBy('identifier')->orderByDesc('created_at')->get()->keyBy('identifier');

            // Get tunnels
            $tunnels = MercuryTunnel::withTrashed()
                ->leftJoin('mercury_appliances', 'mercury_appliances.id', '=', 'appliance_id')
                ->leftJoin('asns', 'asns.id', '=', 'mercury_tunnels.asn_id')
                ->where('mercury_appliances.tag', '=', $_tag)
                ->whereNull('mercury_appliances.deleted_at')
                ->where([['bgp_state', '!=', 'DELETED'], ['tunnel_state', '!=', 'DELETED']])
                ->where('mercury_tunnels.id', '!=', '62')
                ->where(function($x) {
                    $x->where('mercury_tunnels.deleted_at', '>', Carbon::now()->subHour())->orWhereNull('mercury_tunnels.deleted_at');
                })
                ->select(
                    'mercury_tunnels.id',
                    'type',
                    'local_address',
                    'remote_address',
                    'remote_port',
                    'gsl_device_v4',
                    'gsl_device_v6',
                    'client_device_v4',
                    'client_device_v6',
                    'allow_announcements',
                    'allow_downstreaming',
                    'no_bgp',
                    'asns.asn',
                    'mercury_tunnels.organisation_id',
                    'billing_state',
                    'bgp_state',
                    'tunnel_state'
                )
                ->get();

            $sane_tunnels = [];

            foreach($tunnels as $tunnel) {
                $org_id = $tunnel['organisation_id'];
                
                $sane_tunnels[] = [
                    'id'                    => (int)$tunnel['id'],
                    'description'           => substr($tunnel['type'],0,3) . '-' . $tunnel['id'],
                    'type'                  => $tunnel['type'],
                    'local'                 => $tunnel['local_address'],
                    'remote'                => $tunnel['remote_address'],
                    'destination-port'      => $tunnel['remote_port'],
                    'gsl-endpoint-v4'       => $tunnel['gsl_device_v4'],
                    'gsl-endpoint-v6'       => $tunnel['gsl_device_v6'],
                    'client-endpoint-v4'    => $tunnel['client_device_v4'],
                    'client-endpoint-v6'    => $tunnel['client_device_v6'],
                    'asn'                   => (int)$tunnel['asn'],
                    'speed'                 => (int) 7500,
                    'limit-to'              => isset($speed_limits["MER-L:$org_id"]) ? (int) $speed_limits["MER-L:$org_id"]->description : null,
                    'allow-announcements'   => $tunnel['allow_announcements'] ?? $organisation_attributes[$org_id]->allow_announcements ?? true,
                    'allow-downstreaming'   => $tunnel['allow_downstreaming'] ?? $organisation_attributes[$org_id]->allow_downstreaming ?? false,
                    'allow-unknowns'        => $tunnel['allow_unknowns'] ?? $organisation_attributes[$org_id]->allow_unknowns ?? false,
                    'tunnel-state'          => $tunnel['tunnel_state'],
                    'bgp-state'             => $tunnel['bgp_state'],
                    'billing-state'         => $tunnel['billing_state'],
                    'no-bgp'                => $tunnel['no_bgp'] ?? false,
                ];

            }

            return response()->json([
                'tunnels' => $sane_tunnels
            ], 200);
        }
        catch(\Exception $ex) {
            \Log::error($ex);
            return response()->json([
                'tunnels' => []
            ], 500);
        }
    }

    public function register_appliance(Request $_r) {

        // Validate this request in the first place.
        $valid_request = !Validator::make( $_r->all(), [
            'tag'       => ['required', 'string'],
            'server_v4' => ['required', 'regex:/^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.)){3}(25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\/((3[0-2])|([1-2][0-9])|([8-9])))?$/'],
            'server_v6' => ['required', 'regex:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/((1([0-1][0-9]|2[0-8]))|([1-9][0-9])|([8-9])))?$/'],
            'gateway'   => ['required', 'regex:/^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.)){3}(25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\/((3[0-2])|([1-2][0-9])|([8-9])))?$/'],
            'limit'     => ['required', 'integer'],
            'name'      => ['required', 'string']
            ])->fails();

        if($valid_request) {
            $tag = $_r->input('tag');
            $name = $_r->input('name');
            $server_v4 = $_r->input('server_v4');
            $server_v6 = $_r->input('server_v6');
            $gateway = $_r->input('gateway');
            $limit = $_r->input('limit');

            $appliance_existing = MercuryAppliance::where('tag', '=', $tag)->get()->first();

            if($appliance_existing) {
                MercuryAppliance::where('tag', '=', $tag)->update([
                    'reported_at'   => Carbon::now()
                ]);
            }
            else {
                MercuryAppliance::create([
                    'name'          => $name,
                    'tag'           => $tag,
                    'server_v4'     => $server_v4,
                    'server_v6'     => $server_v6,
                    'gateway'       => $gateway,
                    'limit'         => $limit,
                    'reported_at'   => Carbon::now()
                ])->save();

                Notification::route('slack', env('GOLIATH_SLACK_HOOK'))->notify(new SlackNotification("Mercury Appliance $tag has registered with the following information - Name: $name, IPv4: $server_v4, IPv6: $server_v6, Gateway: $gateway, Limit: $limit."));
            }

            return response()->json([
                'message' => 'success'
            ], 200);
        }

        return response()->json([
            'message' => 'failure'
        ], 400);
    }

    public function stream_information(Request $_r) {

    }

    public function upload_summary(Request $_r, $_tag) {

        if($_r->file()) {
            $file_name = "MER-$_tag-BGP-" . Carbon::now()->timestamp . '.conf';
            $file_hash = array_keys($_r->file())[0];

            $appliance_id = MercuryAppliance::where('tag', '=', $_tag)->get()->first()->id;
        
            if(MercuryBgpSnapshot::where('appliance_id', '=', $appliance_id)->get()->count() == 0 || MercuryBgpSnapshot::where('appliance_id', '=', $appliance_id)->orderByDesc('created_at')->get()->first()->checksum != $file_hash) {
                $_r->file($file_hash)->storeAs('confs', $file_name);
    
                // Associate our stored file...
                MercuryBgpSnapshot::create([
                    'appliance_id' => $appliance_id,
                    'location' => "confs/$file_name",
                    'checksum' => $file_hash
                ])->save();
            }
        }
        else {
            // Handle work summary
            foreach($_r->input('summary', []) as $t_id => $t_attr) {
                $to_update = [];

                $attr_key_to_db_key = [
                    'billing-state' => 'billing_state',
                    'bgp-state'     => 'bgp_state',
                    'tunnel-state'  => 'tunnel_state'
                ];

                foreach($attr_key_to_db_key as $attr_key => $db_key) {
                    if(isset($t_attr[$attr_key])) {
                        $to_update[$db_key] = $t_attr[$attr_key];
                    }
                }

                if($to_update !== []) {
                    if($_tag == 'NYC-1') {
                        \Log::debug($to_update);
                    }
                    MercuryTunnel::withTrashed()->where('id', '=', $t_id)->update($to_update);
                }
            }
        }

        return response()->json(['message' => 'success'], 200);   
    }
}
