<?php

namespace App\Http\Controllers\Locus;

// Laravel Specifics
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

// Creatia Core
use App\Helpers\Utility\WhoIs;

// Module Specifics
use App\Models\Prefixes;
use App\Models\GeoIp\PrefixGeoIp;

use App\Http\Controllers\Network\NetworkController;
use App\Models\GeoIp\GeoIpOrganisationAttributes;

class GeoIpController extends NetworkController
{

    public function get_geoips(Request $_r, $_network_id = null) {
        $USER_ORGANISATION_ID = Auth::user()->organisation_id;
        
        // If user has access to anything.
        if($_network_id == null || Prefixes::getOrganisationHasPrefix($USER_ORGANISATION_ID, $_network_id)) {
            return $this->paged_table($_r, function() use($USER_ORGANISATION_ID, $_network_id) {

                // Get every single relevant prefix
                $prefixes_relevant = Prefixes::getOrganisationPrefixes($USER_ORGANISATION_ID)->pluck('id');

                // Construct our baseline query.
                $prefix_query = Prefixes::select('prefixes.id', 'address', 'mask', 'prefixes.name', 'still_announced', 'country', 'region', 'city', 'whois_error', 'prefixes.organisation_id', 'asn')
                    ->selectRaw('(SELECT COUNT(*) FROM supernets WHERE supernet_id = prefixes.id) AS "subnets"')
                    ->leftJoin('prefix_geo_ip', 'prefix_id', '=', 'prefixes.id')
                    ->leftJoin('asns', 'prefixes.asn_id', '=', 'asns.id')
                    ->whereIn('prefixes.id', $prefixes_relevant);

                // Thin it out to relevant subnets only if applicable.
                if($_network_id !== null) {
                    $prefix_query = $prefix_query->leftJoin('supernets', 'prefixes.id', '=', 'supernets.subnet_id')->where('supernets.supernet_id', '=', $_network_id);
                }

                return $prefix_query;
            }, [ 
                'asn'           => [ 'db' => 'asns.asn', 'searchable' => true ],
                'name'          => [ 'db' => 'prefixes.name', 'searchable' => true ],
                'network'       => [ 'db' => DB::raw("CONCAT(address, '/', mask)"), 'searchable' => true ]
            ], 'prefixes.created_at', function($data) {

                $parent_announced = Prefixes::leftJoin('supernets', 'supernet_id', '=', 'prefixes.id')
                    ->whereIn('subnet_id', $data->pluck('id'))
                    ->where('still_announced', '=', 1)
                    ->select('subnet_id')
                    ->distinct()
                    ->get()
                    ->keyBy('subnet_id');

                $data->transform(function($row) use($parent_announced) {

                    if($row['still_announced'] == 0 && isset($parent_announced[$row['id']])) {
                        $row['still_announced'] = 1;
                    }

                    return $row;
                });

                return $data;
            });
        }

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

    public function get_regions(Request $_r) {
        $raw_codes = json_decode(file_get_contents(app_path('Models/GeoIp/RegionCodes.json')), true);

        return response()->json($raw_codes, 200);
    }

    public function get_data($_geo_ip_id) {
        try {
            if($x = PrefixGeoIp::where([['id', '=', $_geo_ip_id], ['organisation_id', '=', Auth::user()->organisation_id]])->first()) {
                return response()->json([
                    'name' => $x->name,
                    'country' => $x->country,
                    'region' => $x->region,
                    'city' => $x->city
                ], 200);
            }
        } catch(\Exception $ex) {}

        return response()->json(['detail' => __('api.500_GENERIC')], 500);
    }

    public function put_data(Request $_r, $_geo_ip_id) {
        try {
            $region_lookup_array = json_decode(file_get_contents(app_path('Models/GeoIp/RegionCodes.json')), true);
            $region_keys = array_keys($region_lookup_array);

            // Validate inputs
            $valid_request = !Validator::make([
                'name'          => $_r->input('name'),
                'country'       => $_r->input('country'),
                'region'        => $_r->input('region'),
                'city'          => $_r->input('city')
                ], [
                'name'          => ['string', 'nullable'],
                'country'       => ['required', Rule::in(array_merge($region_keys, ['0']))],
                'region'        => $_r->input('country') != 0 ? ['required', Rule::in(array_keys($region_lookup_array[$_r->input('country')]['divisions']))] : [],
                'city'          => $_r->input('country') != 0 ? ['required', Rule::in($region_lookup_array[$_r->input('country')]['divisions'][$_r->input('region')]['cities'])] : []
            ])->fails();

            // If valid, update
            if($valid_request) {
                $network = Prefixes::getOrganisationHasPrefix(Auth::user()->organisation_id, $_geo_ip_id);
                $geo_ip_model = PrefixGeoIp::where('prefix_id', '=', $_geo_ip_id)->first();
                $announced_supernet = Prefixes::getIsInAnnouncedSupernet($_geo_ip_id);

                if($network && ($network->still_announced == 1 || $announced_supernet)) {
                    
                    // Determine who-is state
                    $who_is_country = WhoIs::location_lookup("$network->address/$network->mask");

                    $who_is_outcome = ($who_is_country == 'NO_ENTRY' || $who_is_country == 'NO_COUNTRY') ? $who_is_country : (array_search($_r->input('country',$geo_ip_model->country), $who_is_country) ? null : 'NOT_ALIGNED');

                    $network->name = $_r->input('name');

                    $geo_ip_model->fill($_r->input('country') == 0 ? [
                        'country'       => null,
                        'region'        => null,
                        'city'          => null,
                        'whois_error'   => null
                    ] : [
                        'country'       => $_r->input('country', $geo_ip_model->country),
                        'region'        => $_r->input('region', $geo_ip_model->region),
                        'city'          => $_r->input('city', $geo_ip_model->city),
                        'whois_error'   => $who_is_outcome
                    ]);
    
                    if($geo_ip_model->save() && $network->save()) {
                        return response()->json(['detail' => $who_is_outcome ? $who_is_outcome : __('api.200_GENERIC')], 200);
                    }
                    
                    return response()->json(['detail' => __('api.500_FAILURE')], 500);
                }

                return response()->json(['detail' => __('api.404_INVALID_IDENTIFIER', ['resource' => 'GeoIP'])], 404);
            }

            return response()->json(['detail' => __('api.400_INVALID_PAYLOAD')], 400);
        } catch(\Exception $ex) {}

        return response()->json(['detail' => __('api.500_GENERIC')], 500);
    }

    public function get_geoip_metadata(Request $_r, $_geo_ip_id) {

        $USER_ORGANISATION_ID = Auth::user()->organisation_id;

        try {
            $network = Prefixes::getOrganisationHasPrefix($USER_ORGANISATION_ID, $_geo_ip_id);

            if($network) {
        
                return response()->json([
                    'prefix_name' => $network->name,
                    'prefix_address' => "$network->address/$network->mask",
                    'valid_contributor' => Prefixes::getOrganisationHasPrefix($USER_ORGANISATION_ID, $_geo_ip_id),
                ], 200);

            }

            return response()->json(['detail' => __('api.400_INVALID_PAYLOAD')], 400);
        }
        catch(\Exception $ex) {}

        return response()->json(['detail' => __('api.500_GENERIC')], 500);
    }

    public function post_subnet(Request $_r, $_geo_ip_id) {
        $inputs = [
            'country' => $_r->input('country'),
            'region' => $_r->input('region'),
            'city' => $_r->input('city')
        ];

        $region_lookup_array = json_decode(file_get_contents(app_path('Models/GeoIp/RegionCodes.json')), true);
        $region_keys = array_keys($region_lookup_array);

        $rules = [
            'country'       => ['required', Rule::in($region_keys)],
            'region'        => ['required', Rule::in(array_keys($region_lookup_array[$_r->input('country')]['divisions']))],
            'city'          => ['required', Rule::in($region_lookup_array[$_r->input('country')]['divisions'][$_r->input('region')]['cities'])]
        ];

        return NetworkController::post_prefix($_r, $_geo_ip_id, ['input' => $inputs, 'rule' => $rules ] , function($new, $parent, $request) {
            
            $who_is_country = WhoIs::location_lookup($request->input('cidr'));

            $who_is_outcome = ($who_is_country == 'NO_ENTRY' || $who_is_country == 'NO_COUNTRY') ? 
                $who_is_country 
                : 
                (
                    array_search($request->input('country', $new->country), $who_is_country) ? 
                        null
                        :
                        'NOT_ALIGNED'
                ); 

            PrefixGeoIp::create([
                'prefix_id'         => $new->id,
                'country'           => $request->input('country'),
                'region'            => $request->input('region'),
                'city'              => $request->input('city'),
                'organisation_id'   => $parent->organisation_id,
                'whois_error'       => $who_is_outcome
            ]);
            
        });
    }
}
